diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7fbc26f212..513e2f0fcd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -143,4 +143,10 @@ target_sources(${LRS_TARGET} "${CMAKE_CURRENT_LIST_DIR}/points.h" "${CMAKE_CURRENT_LIST_DIR}/depth-sensor.h" "${CMAKE_CURRENT_LIST_DIR}/color-sensor.h" + "${CMAKE_CURRENT_LIST_DIR}/callback-invocation.h" + "${CMAKE_CURRENT_LIST_DIR}/easyloggingpp.h" + "${CMAKE_CURRENT_LIST_DIR}/librealsense-exception.h" + "${CMAKE_CURRENT_LIST_DIR}/polling-device-watcher.h" + "${CMAKE_CURRENT_LIST_DIR}/small-heap.h" + "${CMAKE_CURRENT_LIST_DIR}/basics.h" ) diff --git a/src/archive.h b/src/archive.h index 334c280525..2d09c1f82d 100644 --- a/src/archive.h +++ b/src/archive.h @@ -5,6 +5,8 @@ #include "types.h" #include "core/streaming.h" +#include "callback-invocation.h" + namespace librealsense { diff --git a/src/backend.h b/src/backend.h index 729b35091c..9f7831b0de 100644 --- a/src/backend.h +++ b/src/backend.h @@ -2,11 +2,9 @@ // Copyright(c) 2015 Intel Corporation. All Rights Reserved. #pragma once -#ifndef LIBREALSENSE_BACKEND_H -#define LIBREALSENSE_BACKEND_H -#include "../include/librealsense2/h/rs_types.h" // Inherit all type definitions in the public API -#include "../include/librealsense2/h/rs_option.h" +#include // Inherit all type definitions in the public API +#include #include "usb/usb-types.h" #include "usb/usb-device.h" #include "hid/hid-types.h" @@ -825,6 +823,5 @@ namespace librealsense } double monotonic_to_realtime(double monotonic); -} -#endif +} // namespace librealsense diff --git a/src/basics.h b/src/basics.h new file mode 100644 index 0000000000..ebc1c46132 --- /dev/null +++ b/src/basics.h @@ -0,0 +1,16 @@ +// License: Apache 2.0. See LICENSE file in root directory. +// Copyright(c) 2021 Intel Corporation. All Rights Reserved. + +#pragma once + + +// Disable declspec(dllexport) warnings: +// Classes exported via LRS_EXTENSION_API are **not** part of official librealsense API (at least for now) +// Any extension relying on these APIs must be compiled and distributed together with realsense2.dll +#pragma warning(disable : 4275) /* disable: C4275: non dll-interface class used as base for dll-interface class */ +#pragma warning(disable : 4251) /* disable: C4251: class needs to have dll-interface to be used by clients of class */ +#ifdef WIN32 +#define LRS_EXTENSION_API __declspec(dllexport) +#else +#define LRS_EXTENSION_API +#endif diff --git a/src/callback-invocation.h b/src/callback-invocation.h new file mode 100644 index 0000000000..8d44fbbfa5 --- /dev/null +++ b/src/callback-invocation.h @@ -0,0 +1,67 @@ +// License: Apache 2.0. See LICENSE file in root directory. +// Copyright(c) 2021 Intel Corporation. All Rights Reserved. + +#pragma once + +#include "small-heap.h" + +#include + + +namespace librealsense { + + +struct callback_invocation +{ + std::chrono::high_resolution_clock::time_point started; + std::chrono::high_resolution_clock::time_point ended; +}; + +typedef small_heap< callback_invocation, 1 > callbacks_heap; + +struct callback_invocation_holder +{ + callback_invocation_holder() + : invocation( nullptr ) + , owner( nullptr ) + { + } + callback_invocation_holder( const callback_invocation_holder & ) = delete; + callback_invocation_holder & operator=( const callback_invocation_holder & ) = delete; + + callback_invocation_holder( callback_invocation_holder && other ) + : invocation( other.invocation ) + , owner( other.owner ) + { + other.invocation = nullptr; + } + + callback_invocation_holder( callback_invocation * invocation, callbacks_heap * owner ) + : invocation( invocation ) + , owner( owner ) + { + } + + ~callback_invocation_holder() + { + if( invocation ) + owner->deallocate( invocation ); + } + + callback_invocation_holder & operator=( callback_invocation_holder && other ) + { + invocation = other.invocation; + owner = other.owner; + other.invocation = nullptr; + return *this; + } + + operator bool() { return invocation != nullptr; } + +private: + callback_invocation * invocation; + callbacks_heap * owner; +}; + + +} // namespace librealsense diff --git a/src/concurrency.h b/src/concurrency.h index 17304829e3..0a9de173bc 100644 --- a/src/concurrency.h +++ b/src/concurrency.h @@ -285,6 +285,8 @@ class dispatcher : _owner(owner) {} + bool was_stopped() const { return _owner->_was_stopped.load(); } + // Replacement for sleep() -- try to sleep for a time, but stop if the // dispatcher is stopped // @@ -296,13 +298,12 @@ class dispatcher using namespace std::chrono; std::unique_lock lock(_owner->_was_stopped_mutex); - auto dispatcher_was_stopped = [&]() { return _owner->_was_stopped.load(); }; - if( dispatcher_was_stopped() ) + if( was_stopped() ) return false; // wait_for() returns "false if the predicate pred still evaluates to false after the // rel_time timeout expired, otherwise true" return ! ( - _owner->_was_stopped_cv.wait_for( lock, sleep_time, dispatcher_was_stopped ) ); + _owner->_was_stopped_cv.wait_for( lock, sleep_time, [&]() { return was_stopped(); } ) ); } }; diff --git a/src/easyloggingpp.h b/src/easyloggingpp.h new file mode 100644 index 0000000000..152d1652b2 --- /dev/null +++ b/src/easyloggingpp.h @@ -0,0 +1,47 @@ +// License: Apache 2.0. See LICENSE file in root directory. +// Copyright(c) 2021 Intel Corporation. All Rights Reserved. + +#pragma once + +#if BUILD_EASYLOGGINGPP + +#include "../third-party/easyloggingpp/src/easylogging++.h" + +#ifdef RS2_USE_ANDROID_BACKEND +#include +#include + +#define LOG_TAG "librs" + +#define LOG_INFO(...) do { std::ostringstream ss; ss << __VA_ARGS__; __android_log_write(librealsense::ANDROID_LOG_INFO, LOG_TAG, ss.str().c_str()); } while(false) +#define LOG_WARNING(...) do { std::ostringstream ss; ss << __VA_ARGS__; __android_log_write(librealsense::ANDROID_LOG_WARN, LOG_TAG, ss.str().c_str()); } while(false) +#define LOG_ERROR(...) do { std::ostringstream ss; ss << __VA_ARGS__; __android_log_write(librealsense::ANDROID_LOG_ERROR, LOG_TAG, ss.str().c_str()); } while(false) +#define LOG_FATAL(...) do { std::ostringstream ss; ss << __VA_ARGS__; __android_log_write(librealsense::ANDROID_LOG_ERROR, LOG_TAG, ss.str().c_str()); } while(false) +#ifdef NDEBUG +#define LOG_DEBUG(...) +#else +#define LOG_DEBUG(...) do { std::ostringstream ss; ss << __VA_ARGS__; __android_log_write(librealsense::ANDROID_LOG_DEBUG, LOG_TAG, ss.str().c_str()); } while(false) +#endif + +#else //RS2_USE_ANDROID_BACKEND + +#define LOG_DEBUG(...) do { CLOG(DEBUG ,"librealsense") << __VA_ARGS__; } while(false) +#define LOG_INFO(...) do { CLOG(INFO ,"librealsense") << __VA_ARGS__; } while(false) +#define LOG_WARNING(...) do { CLOG(WARNING ,"librealsense") << __VA_ARGS__; } while(false) +#define LOG_ERROR(...) do { CLOG(ERROR ,"librealsense") << __VA_ARGS__; } while(false) +#define LOG_FATAL(...) do { CLOG(FATAL ,"librealsense") << __VA_ARGS__; } while(false) + +#endif // RS2_USE_ANDROID_BACKEND + + +#else // BUILD_EASYLOGGINGPP + + +#define LOG_DEBUG(...) do { ; } while(false) +#define LOG_INFO(...) do { ; } while(false) +#define LOG_WARNING(...) do { ; } while(false) +#define LOG_ERROR(...) do { ; } while(false) +#define LOG_FATAL(...) do { ; } while(false) + + +#endif // BUILD_EASYLOGGINGPP diff --git a/src/gl/CMakeLists.txt b/src/gl/CMakeLists.txt index 5f229805f5..1f907da1d9 100644 --- a/src/gl/CMakeLists.txt +++ b/src/gl/CMakeLists.txt @@ -1,7 +1,7 @@ # License: Apache 2.0. See LICENSE file in root directory. # Copyright(c) 2019 Intel Corporation. All Rights Reserved. -cmake_minimum_required(VERSION 2.8.9) +cmake_minimum_required(VERSION 2.8.9...3.20.5) project(realsense2-gl) @@ -88,7 +88,7 @@ configure_package_config_file(../../CMake/realsense2-glConfig.cmake.in realsense configure_file(../../config/librealsense-gl.pc.in ../../config/realsense2-gl.pc @ONLY) -target_link_libraries(${PROJECT_NAME} +target_link_libraries(${PROJECT_NAME} PRIVATE ${DEPENDENCIES} ) diff --git a/src/librealsense-exception.h b/src/librealsense-exception.h new file mode 100644 index 0000000000..138036aab3 --- /dev/null +++ b/src/librealsense-exception.h @@ -0,0 +1,144 @@ +// License: Apache 2.0. See LICENSE file in root directory. +// Copyright(c) 2021 Intel Corporation. All Rights Reserved. + +#pragma once + +#include +#include "easyloggingpp.h" +#include "basics.h" // LRS_EXTENSION_API + +#include +#include + + +namespace librealsense { + + +class librealsense_exception : public std::exception +{ +public: + const char * get_message() const noexcept { return _msg.c_str(); } + + rs2_exception_type get_exception_type() const noexcept { return _exception_type; } + + const char * what() const noexcept override { return _msg.c_str(); } + +protected: + librealsense_exception( const std::string & msg, rs2_exception_type exception_type ) noexcept + : _msg( msg ) + , _exception_type( exception_type ) + { + } + +private: + std::string _msg; + rs2_exception_type _exception_type; +}; + + +class LRS_EXTENSION_API recoverable_exception : public librealsense_exception +{ +public: + recoverable_exception( const std::string & msg, rs2_exception_type exception_type ) noexcept; +}; + + +class unrecoverable_exception : public librealsense_exception +{ +public: + unrecoverable_exception( const std::string & msg, rs2_exception_type exception_type ) noexcept + : librealsense_exception( msg, exception_type ) + { + LOG_ERROR( msg ); + } +}; + + +class io_exception : public unrecoverable_exception +{ +public: + io_exception( const std::string & msg ) noexcept + : unrecoverable_exception( msg, RS2_EXCEPTION_TYPE_IO ) + { + } +}; + + +class camera_disconnected_exception : public unrecoverable_exception +{ +public: + camera_disconnected_exception( const std::string & msg ) noexcept + : unrecoverable_exception( msg, RS2_EXCEPTION_TYPE_CAMERA_DISCONNECTED ) + { + } +}; + + +class backend_exception : public unrecoverable_exception +{ +public: + backend_exception( const std::string & msg, rs2_exception_type exception_type ) noexcept + : unrecoverable_exception( msg, exception_type ) + { + } +}; + + +class linux_backend_exception : public backend_exception +{ +public: + linux_backend_exception( const std::string & msg ) noexcept + : backend_exception( generate_last_error_message( msg ), RS2_EXCEPTION_TYPE_BACKEND ) + { + } + +private: + std::string generate_last_error_message( const std::string & msg ) const + { + return msg + " Last Error: " + strerror( errno ); + } +}; + + +class windows_backend_exception : public backend_exception +{ +public: + // TODO: get last error + windows_backend_exception( const std::string & msg ) noexcept + : backend_exception( msg, RS2_EXCEPTION_TYPE_BACKEND ) + { + } +}; + + +class invalid_value_exception : public recoverable_exception +{ +public: + invalid_value_exception( const std::string & msg ) noexcept + : recoverable_exception( msg, RS2_EXCEPTION_TYPE_INVALID_VALUE ) + { + } +}; + + +class wrong_api_call_sequence_exception : public recoverable_exception +{ +public: + wrong_api_call_sequence_exception( const std::string & msg ) noexcept + : recoverable_exception( msg, RS2_EXCEPTION_TYPE_WRONG_API_CALL_SEQUENCE ) + { + } +}; + + +class not_implemented_exception : public recoverable_exception +{ +public: + not_implemented_exception( const std::string & msg ) noexcept + : recoverable_exception( msg, RS2_EXCEPTION_TYPE_NOT_IMPLEMENTED ) + { + } +}; + + +} // namespace librealsense diff --git a/src/libuvc/rsusb-backend-linux.cpp b/src/libuvc/rsusb-backend-linux.cpp index db5e772f1c..85fb57cf60 100644 --- a/src/libuvc/rsusb-backend-linux.cpp +++ b/src/libuvc/rsusb-backend-linux.cpp @@ -3,6 +3,7 @@ #include "rsusb-backend-linux.h" #include "types.h" +#include "../polling-device-watcher.h" #include "../uvc/uvc-device.h" namespace librealsense diff --git a/src/linux/CMakeLists.txt b/src/linux/CMakeLists.txt index 62f0bcd720..4da5bfe9aa 100644 --- a/src/linux/CMakeLists.txt +++ b/src/linux/CMakeLists.txt @@ -13,3 +13,16 @@ target_link_libraries(${LRS_TARGET} PRIVATE usb) if(USE_EXTERNAL_USB) add_dependencies(${LRS_TARGET} libusb) endif() + +include(${CMAKE_CURRENT_LIST_DIR}/find_udev.cmake) +if(UDEV_FOUND) + target_sources(${LRS_TARGET} + PRIVATE + "${CMAKE_CURRENT_LIST_DIR}/udev-device-watcher.cpp" + "${CMAKE_CURRENT_LIST_DIR}/udev-device-watcher.h" + ) + target_link_libraries(${LRS_TARGET} PRIVATE udev) + add_definitions(-DUSING_UDEV) +else() + message(STATUS "UDEV not found; using polling device-watcher!") +endif() diff --git a/src/linux/backend-v4l2.cpp b/src/linux/backend-v4l2.cpp index b80b0b7455..e47f9791f1 100644 --- a/src/linux/backend-v4l2.cpp +++ b/src/linux/backend-v4l2.cpp @@ -5,6 +5,11 @@ #include "backend-hid.h" #include "backend.h" #include "types.h" +#if defined(USING_UDEV) +#include "udev-device-watcher.h" +#else +#include "../polling-device-watcher.h" +#endif #include "usb/usb-enumerator.h" #include "usb/usb-device.h" @@ -1848,7 +1853,11 @@ namespace librealsense std::shared_ptr v4l_backend::create_device_watcher() const { - return std::make_shared(this); +#if defined(USING_UDEV) + return std::make_shared< udev_device_watcher >( this ); +#else + return std::make_shared< polling_device_watcher >( this ); +#endif } std::shared_ptr create_backend() diff --git a/src/linux/find_udev.cmake b/src/linux/find_udev.cmake new file mode 100644 index 0000000000..09e1c1294f --- /dev/null +++ b/src/linux/find_udev.cmake @@ -0,0 +1,25 @@ +# Try to find UDEV +# Once done, this will define +# +# UDEV_FOUND - system has UDEV +# UDEV_INCLUDE_DIRS - the UDEV include directories +# UDEV_LIBRARIES - the UDEV library +find_package(PkgConfig) + +pkg_check_modules(UDEV_PKGCONF libudev) + +find_path(UDEV_INCLUDE_DIRS + NAMES libudev.h + PATHS ${UDEV_PKGCONF_INCLUDE_DIRS} +) + + +find_library(UDEV_LIBRARIES + NAMES udev + PATHS ${UDEV_PKGCONF_LIBRARY_DIRS} +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Udev DEFAULT_MSG UDEV_INCLUDE_DIRS UDEV_LIBRARIES) + +mark_as_advanced(UDEV_INCLUDE_DIRS UDEV_LIBRARIES) diff --git a/src/linux/udev-device-watcher.cpp b/src/linux/udev-device-watcher.cpp new file mode 100644 index 0000000000..b5efb9161f --- /dev/null +++ b/src/linux/udev-device-watcher.cpp @@ -0,0 +1,224 @@ +// License: Apache 2.0. See LICENSE file in root directory. +// Copyright(c) 2021 Intel Corporation. All Rights Reserved. + +#include "udev-device-watcher.h" + +#include + +#include +#include + +using std::string; +using std::runtime_error; + + +namespace { + + + void foreach_device_prop( struct udev_device * udev_dev, + std::function< void( std::string const & param, std::string const & value ) > callback ) + { + struct udev_list_entry * prop_entry; + udev_list_entry_foreach( prop_entry, udev_device_get_properties_list_entry( udev_dev ) ) + { + const string prop_name = udev_list_entry_get_name( prop_entry ); + const string prop_value = udev_list_entry_get_value( prop_entry ); + + callback( prop_name, prop_value ); + } + } + + + std::string udev_string( struct udev_device * dev ) + { + std::ostringstream os; + if( ! dev ) + os << "null"; + else + { + char const * sysname = udev_device_get_sysname( dev ); + char const * model = udev_device_get_property_value( dev, "ID_MODEL" ); + if( ! model || strncmp( model, "Intel", 5 )) + model = sysname; + os << model; + if( model != sysname ) + os << " [" << sysname << ']'; + //os << udev_device_get_syspath( dev ); + + // Enable to get a list of all properties + // For usb_device: + // ACTION=add BUSNUM=002 DEVNAME=/dev/bus/usb/002/125 DEVNUM=125 + // DEVPATH=/devices/pci0000:00/0000:00:14.0/usb2/2-2/2-2.4/2-2.4.4 DEVTYPE=usb_device DRIVER=usb + // ID_BUS=usb ID_MODEL=Intel_R__RealSense_TM__515 + // ID_MODEL_ENC=Intel\x28R\x29\x20RealSense\x28TM\x29\x20515 ID_MODEL_ID=0b64 ID_REVISION=1058 + // ID_SERIAL=Intel_R__RealSense_TM__Camera_Intel_R__RealSense_TM__515_00000000F0070132 + // ID_SERIAL_SHORT=00000000F0070132 ID_USB_INTERFACES=:0e0100:0e0200:ff0000:030000: + // ID_VENDOR=Intel_R__RealSense_TM__Camera + // ID_VENDOR_ENC=Intel\x28R\x29\x20RealSense\x28TM\x29\x20Camera ID_VENDOR_FROM_DATABASE=Intel Corp. + // ID_VENDOR_ID=8086 MAJOR=189 MINOR=252 PRODUCT=8086/b64/1058 SEQNUM=249377 SUBSYSTEM=usb + // TYPE=239/2/1 USEC_INITIALIZED=862653458266 + // And, for usb_interface: + // .MM_USBIFNUM=01 ACTION=add + // DEVPATH=/devices/pci0000:00/0000:00:14.0/usb2/2-2/2-2.4/2-2.4.4/2-2.4.4:1.1 DEVTYPE=usb_interface + // DRIVER=uvcvideo ID_USB_CLASS_FROM_DATABASE=Miscellaneous Device + // ID_USB_PROTOCOL_FROM_DATABASE=Interface Association ID_VENDOR_FROM_DATABASE=Intel Corp. + // INTERFACE=14/2/0 MODALIAS=usb:v8086p0B64d1058dcEFdsc02dp01ic0Eisc02ip00in01 PRODUCT=8086/b64/1058 + // SEQNUM=249386 SUBSYSTEM=usb TYPE=239/2/1 USEC_INITIALIZED=862653461483 +#if 0 + char sep = '['; + foreach_device_prop( dev, [&]( std::string const& name, std::string const & value ) + { + os << sep << name << '=' << value; + sep = ' '; + } ); + if( sep != '[' ) + os << ']'; +#endif + } + return os.str(); + } + } + + +namespace librealsense { + + +udev_device_watcher::udev_device_watcher( const platform::backend * backend ) + : _backend( backend ) + , _active_object( [this]( dispatcher::cancellable_timer timer ) { + struct pollfd fds; + fds.fd = _udev_monitor_fd; + fds.events = POLLIN; + + // Polling will block for a time but we cannot block for too long, as we want destruction to happen in + // reasonable time. So we use a short-enough period: + int const POLLING_PERIOD_MS = 100; + if( poll( &fds, 1, POLLING_PERIOD_MS ) > 0 ) + { + if( timer.was_stopped() || ! _udev_monitor || POLLIN != fds.revents ) + return; + + auto udev_dev = udev_monitor_receive_device( _udev_monitor ); + if( ! udev_dev ) + { + LOG_DEBUG( "failed to read data from udev monitor socket" ); + return; + } + + const string udev_action = udev_device_get_action( udev_dev ); + if( udev_action == "add" || udev_action == "remove" ) + { + LOG_DEBUG( "[udev] " << udev_action << " " << udev_string( udev_dev ) ); + // On remove events, we get all the device interfaces first, and lastly we get the device. + // On add events, we get the device first and only then the device interfaces. + // In any case, we get lots of adds/removes for each device. And we only want to do one enumeration -- + // so we wait for things to calm down and just remember that enumeration is needed... + _changed = true; + } + + udev_device_unref( udev_dev ); + } + else if( _changed ) + { + // Something's changed but nothing's happened in the last polling period -- let's enumerate! + LOG_DEBUG( "[udev] checking ..." ); + platform::backend_device_group curr( _backend->query_uvc_devices(), + _backend->query_usb_devices(), + _backend->query_hid_devices() ); + if( list_changed( _devices_data.uvc_devices, curr.uvc_devices ) + || list_changed( _devices_data.usb_devices, curr.usb_devices ) + || list_changed( _devices_data.hid_devices, curr.hid_devices ) ) + { + LOG_DEBUG( "[udev] changed!" ); + callback_invocation_holder callback = { _callback_inflight.allocate(), &_callback_inflight }; + if( callback ) + _callback( _devices_data, curr ); + _devices_data = curr; + } + _changed = false; + } + } ) +{ + _udev_ctx = udev_new(); + if( ! _udev_ctx ) + throw runtime_error( "could not initialize udev" ); + + _udev_monitor = udev_monitor_new_from_netlink( _udev_ctx, "udev" ); + if( ! _udev_monitor ) + { + udev_unref( _udev_ctx ); + _udev_ctx = nullptr; + throw runtime_error( "could not initialize udev monitor" ); + } + + _udev_monitor_fd = udev_monitor_get_fd( _udev_monitor ); + + if( udev_monitor_filter_add_match_subsystem_devtype( _udev_monitor, "usb", 0 ) ) + { + udev_monitor_unref( _udev_monitor ); + _udev_monitor = nullptr; + _udev_monitor_fd = -1; + udev_unref( _udev_ctx ); + _udev_ctx = nullptr; + throw runtime_error( "could not initialize udev monitor filter for \"usb\" subsystem" ); + } + + if( udev_monitor_enable_receiving( _udev_monitor ) ) + { + udev_monitor_unref( _udev_monitor ); + _udev_monitor = nullptr; + _udev_monitor_fd = -1; + udev_unref( _udev_ctx ); + _udev_ctx = nullptr; + throw runtime_error( "failed to enable the udev monitor" ); + } + + _devices_data = { _backend->query_uvc_devices(), _backend->query_usb_devices(), _backend->query_hid_devices() }; +} + + +udev_device_watcher::~udev_device_watcher() +{ + stop(); + + if( _udev_monitor_fd == -1 ) + throw runtime_error( "monitor fd was -1" ); + + /* Release the udev monitor */ + udev_monitor_unref( _udev_monitor ); + _udev_monitor = nullptr; + _udev_monitor_fd = -1; + + /* Clean up the udev context */ + udev_unref( _udev_ctx ); + _udev_ctx = nullptr; +} + + +// Scan devices using udev +bool udev_device_watcher::foreach_device( std::function< bool( struct udev_device * udev_dev ) > callback ) +{ + auto enumerator = udev_enumerate_new( _udev_ctx ); + if( ! enumerator ) + throw runtime_error( "error creating udev enumerator" ); + + udev_enumerate_add_match_subsystem( enumerator, "usb" ); + udev_enumerate_scan_devices( enumerator ); + + auto devices = udev_enumerate_get_list_entry( enumerator ); + struct udev_list_entry * device_entry; + udev_list_entry_foreach( device_entry, devices ) + { + const char * path = udev_list_entry_get_name( device_entry ); + struct udev_device * udev_dev = udev_device_new_from_syspath( _udev_ctx, path ); + + callback( udev_dev ); + + udev_device_unref( udev_dev ); + } + + udev_enumerate_unref( enumerator ); +} + + +} // namespace librealsense \ No newline at end of file diff --git a/src/linux/udev-device-watcher.h b/src/linux/udev-device-watcher.h new file mode 100644 index 0000000000..beeaca482f --- /dev/null +++ b/src/linux/udev-device-watcher.h @@ -0,0 +1,57 @@ +// License: Apache 2.0. See LICENSE file in root directory. +// Copyright(c) 2021 Intel Corporation. All Rights Reserved. + +#pragma once + +#include "../backend.h" +#include "../concurrency.h" +#include "../callback-invocation.h" + +#include + + +namespace librealsense { + + +class udev_device_watcher : public librealsense::platform::device_watcher +{ + active_object<> _active_object; + + callbacks_heap _callback_inflight; + platform::backend const * _backend; + + platform::backend_device_group _devices_data; + platform::device_changed_callback _callback; + + struct udev * _udev_ctx; + struct udev_monitor * _udev_monitor; + int _udev_monitor_fd; + bool _changed = false; + +public: + udev_device_watcher( platform::backend const * ); + ~udev_device_watcher(); + + // device_watcher +public: + void start( platform::device_changed_callback callback ) override + { + stop(); + _callback = std::move( callback ); + _active_object.start(); + } + + void stop() override + { + _active_object.stop(); + _callback_inflight.wait_until_empty(); + } + + bool is_stopped() const override { return ! _active_object.is_active(); } + +private: + bool foreach_device( std::function< bool( struct udev_device* udev_dev ) > ); +}; + + +} // namespace librealsense diff --git a/src/polling-device-watcher.h b/src/polling-device-watcher.h new file mode 100644 index 0000000000..8c328e4c7a --- /dev/null +++ b/src/polling-device-watcher.h @@ -0,0 +1,77 @@ +// License: Apache 2.0. See LICENSE file in root directory. +// Copyright(c) 2021 Intel Corporation. All Rights Reserved. + +#pragma once + +#include "backend.h" +#include "concurrency.h" +#include "callback-invocation.h" + + +namespace librealsense { + + +// This device_watcher enumerates all devices every set amount of time (POLLING_DEVICES_INTERVAL_MS) +// +class polling_device_watcher : public librealsense::platform::device_watcher +{ +public: + polling_device_watcher( const platform::backend * backend_ref ) + : _backend( backend_ref ) + , _active_object( [this]( dispatcher::cancellable_timer cancellable_timer ) { polling( cancellable_timer ); } ) + , _devices_data() + { + _devices_data = { _backend->query_uvc_devices(), _backend->query_usb_devices(), _backend->query_hid_devices() }; + } + + ~polling_device_watcher() { stop(); } + + void polling( dispatcher::cancellable_timer cancellable_timer ) + { + if( cancellable_timer.try_sleep( std::chrono::milliseconds( POLLING_DEVICES_INTERVAL_MS ) ) ) + { + platform::backend_device_group curr( _backend->query_uvc_devices(), + _backend->query_usb_devices(), + _backend->query_hid_devices() ); + if( list_changed( _devices_data.uvc_devices, curr.uvc_devices ) + || list_changed( _devices_data.usb_devices, curr.usb_devices ) + || list_changed( _devices_data.hid_devices, curr.hid_devices ) ) + { + callback_invocation_holder callback = { _callback_inflight.allocate(), &_callback_inflight }; + if( callback ) + { + _callback( _devices_data, curr ); + _devices_data = curr; + } + } + } + } + + void start( platform::device_changed_callback callback ) override + { + stop(); + _callback = std::move( callback ); + _active_object.start(); + } + + void stop() override + { + _active_object.stop(); + + _callback_inflight.wait_until_empty(); + } + + bool is_stopped() const override { return ! _active_object.is_active(); } + +private: + active_object<> _active_object; + + callbacks_heap _callback_inflight; + const platform::backend * _backend; + + platform::backend_device_group _devices_data; + platform::device_changed_callback _callback; +}; + + +} // namespace librealsense diff --git a/src/small-heap.h b/src/small-heap.h new file mode 100644 index 0000000000..1629acb8cf --- /dev/null +++ b/src/small-heap.h @@ -0,0 +1,106 @@ +// License: Apache 2.0. See LICENSE file in root directory. +// Copyright(c) 2021 Intel Corporation. All Rights Reserved. + +#pragma once + +#include "librealsense-exception.h" + +#include +#include + + +namespace librealsense { + + +template < class T, int C > +class small_heap +{ + T buffer[C]; + bool is_free[C]; + std::mutex mutex; + bool keep_allocating = true; + std::condition_variable cv; + int size = 0; + +public: + static const int CAPACITY = C; + + small_heap() + { + for( auto i = 0; i < C; i++ ) + { + is_free[i] = true; + buffer[i] = std::move( T() ); + } + } + + T * allocate() + { + std::unique_lock< std::mutex > lock( mutex ); + if( ! keep_allocating ) + return nullptr; + + for( auto i = 0; i < C; i++ ) + { + if( is_free[i] ) + { + is_free[i] = false; + size++; + return &buffer[i]; + } + } + return nullptr; + } + + void deallocate( T * item ) + { + if( item < buffer || item >= buffer + C ) + { + throw invalid_value_exception( "Trying to return item to a heap that didn't allocate it!" ); + } + auto i = item - buffer; + auto old_value = std::move( buffer[i] ); + buffer[i] = std::move( T() ); + + { + std::unique_lock< std::mutex > lock( mutex ); + + is_free[i] = true; + size--; + + if( size == 0 ) + { + lock.unlock(); + cv.notify_one(); + } + } + } + + void stop_allocation() + { + std::unique_lock< std::mutex > lock( mutex ); + keep_allocating = false; + } + + void wait_until_empty() + { + std::unique_lock< std::mutex > lock( mutex ); + + const auto ready = [this]() { + return is_empty(); + }; + if( ! ready() + && ! cv.wait_for( lock, + std::chrono::hours( 1000 ), + ready ) ) // for some reason passing std::chrono::duration::max makes it return instantly + { + throw invalid_value_exception( "Could not flush one of the user controlled objects!" ); + } + } + + bool is_empty() const { return size == 0; } + int get_size() const { return size; } +}; + + +} // namespace librealsense diff --git a/src/types.h b/src/types.h index 463e82f515..a42690afca 100644 --- a/src/types.h +++ b/src/types.h @@ -6,21 +6,15 @@ // out of this file and into more appropriate locations. #pragma once -#ifndef LIBREALSENSE_TYPES_H -#define LIBREALSENSE_TYPES_H - -// Disable declspec(dllexport) warnings: -// Classes exported via LRS_EXTENSION_API are **not** part of official librealsense API (at least for now) -// Any extension relying on these APIs must be compiled and distributed together with realsense2.dll -#pragma warning(disable : 4275) /* disable: C4275: non dll-interface class used as base for dll-interface class */ -#pragma warning(disable : 4251) /* disable: C4251: class needs to have dll-interface to be used by clients of class */ -#ifdef WIN32 -#define LRS_EXTENSION_API __declspec(dllexport) -#else -#define LRS_EXTENSION_API -#endif -#include "../include/librealsense2/hpp/rs_types.hpp" +#include "basics.h" + +#include + +#include "backend.h" +#include "concurrency.h" +#include "librealsense-exception.h" +#include "easyloggingpp.h" #ifndef _USE_MATH_DEFINES #define _USE_MATH_DEFINES @@ -42,12 +36,6 @@ #include // For std::forward #include #include -#include "backend.h" -#include "concurrency.h" - -#if BUILD_EASYLOGGINGPP -#include "../third-party/easyloggingpp/src/easylogging++.h" -#endif // BUILD_EASYLOGGINGPP typedef unsigned char byte; @@ -207,43 +195,6 @@ namespace librealsense void reset_logger(); void enable_rolling_log_file( unsigned max_size ); -#if BUILD_EASYLOGGINGPP - -#ifdef RS2_USE_ANDROID_BACKEND -#include - -#define LOG_TAG "librs" - -#define LOG_INFO(...) do { std::stringstream ss; ss << __VA_ARGS__; __android_log_write(librealsense::ANDROID_LOG_INFO, LOG_TAG, ss.str().c_str()); } while(false) -#define LOG_WARNING(...) do { std::stringstream ss; ss << __VA_ARGS__; __android_log_write(librealsense::ANDROID_LOG_WARN, LOG_TAG, ss.str().c_str()); } while(false) -#define LOG_ERROR(...) do { std::stringstream ss; ss << __VA_ARGS__; __android_log_write(librealsense::ANDROID_LOG_ERROR, LOG_TAG, ss.str().c_str()); } while(false) -#define LOG_FATAL(...) do { std::stringstream ss; ss << __VA_ARGS__; __android_log_write(librealsense::ANDROID_LOG_ERROR, LOG_TAG, ss.str().c_str()); } while(false) -#ifdef NDEBUG -#define LOG_DEBUG(...) -#else -#define LOG_DEBUG(...) do { std::stringstream ss; ss << __VA_ARGS__; __android_log_write(librealsense::ANDROID_LOG_DEBUG, LOG_TAG, ss.str().c_str()); } while(false) -#endif - -#else //RS2_USE_ANDROID_BACKEND - -#define LOG_DEBUG(...) do { CLOG(DEBUG ,"librealsense") << __VA_ARGS__; } while(false) -#define LOG_INFO(...) do { CLOG(INFO ,"librealsense") << __VA_ARGS__; } while(false) -#define LOG_WARNING(...) do { CLOG(WARNING ,"librealsense") << __VA_ARGS__; } while(false) -#define LOG_ERROR(...) do { CLOG(ERROR ,"librealsense") << __VA_ARGS__; } while(false) -#define LOG_FATAL(...) do { CLOG(FATAL ,"librealsense") << __VA_ARGS__; } while(false) - -#endif // RS2_USE_ANDROID_BACKEND - -#else // BUILD_EASYLOGGINGPP - - #define LOG_DEBUG(...) do { ; } while(false) -#define LOG_INFO(...) do { ; } while(false) -#define LOG_WARNING(...) do { ; } while(false) -#define LOG_ERROR(...) do { ; } while(false) -#define LOG_FATAL(...) do { ; } while(false) - -#endif // BUILD_EASYLOGGINGPP - // Enhancement for debug mode that incurs performance penalty with STL // std::clamp to be introduced with c++17 template< typename T> @@ -258,129 +209,6 @@ namespace librealsense #endif } - ////////////////////////// - // Exceptions mechanism // - ////////////////////////// - - - class librealsense_exception : public std::exception - { - public: - const char* get_message() const noexcept - { - return _msg.c_str(); - } - - rs2_exception_type get_exception_type() const noexcept - { - return _exception_type; - } - - const char* what() const noexcept override - { - return _msg.c_str(); - } - - protected: - librealsense_exception(const std::string& msg, - rs2_exception_type exception_type) noexcept - : _msg(msg), - _exception_type(exception_type) - {} - - private: - std::string _msg; - rs2_exception_type _exception_type; - }; - - class LRS_EXTENSION_API recoverable_exception : public librealsense_exception - { - public: - recoverable_exception(const std::string& msg, - rs2_exception_type exception_type) noexcept; - }; - - class unrecoverable_exception : public librealsense_exception - { - public: - unrecoverable_exception(const std::string& msg, - rs2_exception_type exception_type) noexcept - : librealsense_exception(msg, exception_type) - { - LOG_ERROR(msg); - } - }; - class io_exception : public unrecoverable_exception - { - public: - io_exception(const std::string& msg) noexcept - : unrecoverable_exception(msg, RS2_EXCEPTION_TYPE_IO) - {} - }; - class camera_disconnected_exception : public unrecoverable_exception - { - public: - camera_disconnected_exception(const std::string& msg) noexcept - : unrecoverable_exception(msg, RS2_EXCEPTION_TYPE_CAMERA_DISCONNECTED) - {} - }; - - class backend_exception : public unrecoverable_exception - { - public: - backend_exception(const std::string& msg, - rs2_exception_type exception_type) noexcept - : unrecoverable_exception(msg, exception_type) - {} - }; - - class linux_backend_exception : public backend_exception - { - public: - linux_backend_exception(const std::string& msg) noexcept - : backend_exception(generate_last_error_message(msg), RS2_EXCEPTION_TYPE_BACKEND) - {} - - private: - std::string generate_last_error_message(const std::string& msg) const - { - return msg + " Last Error: " + strerror(errno); - } - }; - - class windows_backend_exception : public backend_exception - { - public: - // TODO: get last error - windows_backend_exception(const std::string& msg) noexcept - : backend_exception(msg, RS2_EXCEPTION_TYPE_BACKEND) - {} - }; - - class invalid_value_exception : public recoverable_exception - { - public: - invalid_value_exception(const std::string& msg) noexcept - : recoverable_exception(msg, RS2_EXCEPTION_TYPE_INVALID_VALUE) - {} - }; - - class wrong_api_call_sequence_exception : public recoverable_exception - { - public: - wrong_api_call_sequence_exception(const std::string& msg) noexcept - : recoverable_exception(msg, RS2_EXCEPTION_TYPE_WRONG_API_CALL_SEQUENCE) - {} - }; - - class not_implemented_exception : public recoverable_exception - { - public: - not_implemented_exception(const std::string& msg) noexcept - : recoverable_exception(msg, RS2_EXCEPTION_TYPE_NOT_IMPLEMENTED) - {} - }; - #pragma pack(push, 1) template class big_endian @@ -689,10 +517,6 @@ namespace librealsense // Pixel formats // /////////////////// - typedef std::tuple native_pixel_format_tuple; - typedef std::tuple output_tuple; - typedef std::tuple> request_mapping_tuple; - struct resolution { uint32_t width, height; @@ -762,76 +586,6 @@ namespace librealsense resolution_func stream_resolution; }; - struct pixel_format_unpacker - { - bool requires_processing; - void(*unpack)(byte * const dest[], const byte * source, int width, int height, int actual_size, int input_size); - std::vector outputs; - - platform::stream_profile get_uvc_profile(const stream_profile& request, uint32_t fourcc, const std::vector& uvc_profiles) const - { - platform::stream_profile uvc_profile{}; - auto it = std::find_if(begin(uvc_profiles), end(uvc_profiles), - [&fourcc, &request, this](const platform::stream_profile& uvc_p) - { - for (auto & o : outputs) - { - auto res = o.stream_resolution(resolution{ uvc_p.width, uvc_p.height }); - if (o.stream_desc.type == request.stream && o.stream_desc.index == request.index && - res.width == request.width && res.height == request.height && - uvc_p.format == fourcc && request.fps == uvc_p.fps) - return true; - } - return false; - }); - if (it != end(uvc_profiles)) - { - uvc_profile = *it; - } - return uvc_profile; - } - - bool satisfies(const stream_profile& request, uint32_t fourcc, const std::vector& uvc_profiles) const - { - auto uvc_profile = get_uvc_profile(request, fourcc, uvc_profiles); - return provides_stream(request, fourcc, uvc_profile) && - get_format(request.stream, request.index) == request.format; - } - - bool provides_stream(const stream_profile& request, uint32_t fourcc, const platform::stream_profile& uvc_profile) const - { - for (auto& o : outputs) - { - auto res = o.stream_resolution(resolution{ uvc_profile.width, uvc_profile.height }); - if (o.stream_desc.type == request.stream && o.stream_desc.index == request.index && - res.width == request.width && res.height == request.height) - return true; - } - - return false; - } - rs2_format get_format(rs2_stream stream, int index) const - { - for (auto& o : outputs) - if (o.stream_desc.type == stream && o.stream_desc.index == index) - return o.format; - - throw invalid_value_exception("missing output"); - } - - operator std::vector() - { - std::vector tuple_outputs; - - for (auto output : outputs) - { - tuple_outputs.push_back(std::make_tuple(output.stream_desc.type, output.stream_desc.index, output.format)); - } - return tuple_outputs; - } - - }; - class stream_profile_interface; class frame_interface; @@ -1199,93 +953,6 @@ namespace librealsense return (c0 << 24) | (c1 << 16) | (c2 << 8) | c3; } - template - class small_heap - { - T buffer[C]; - bool is_free[C]; - std::mutex mutex; - bool keep_allocating = true; - std::condition_variable cv; - int size = 0; - - public: - static const int CAPACITY = C; - - small_heap() - { - for (auto i = 0; i < C; i++) - { - is_free[i] = true; - buffer[i] = std::move(T()); - } - } - - T * allocate() - { - std::unique_lock lock(mutex); - if (!keep_allocating) return nullptr; - - for (auto i = 0; i < C; i++) - { - if (is_free[i]) - { - is_free[i] = false; - size++; - return &buffer[i]; - } - } - return nullptr; - } - - void deallocate(T * item) - { - if (item < buffer || item >= buffer + C) - { - throw invalid_value_exception("Trying to return item to a heap that didn't allocate it!"); - } - auto i = item - buffer; - auto old_value = std::move(buffer[i]); - buffer[i] = std::move(T()); - - { - std::unique_lock lock(mutex); - - is_free[i] = true; - size--; - - if (size == 0) - { - lock.unlock(); - cv.notify_one(); - } - } - } - - void stop_allocation() - { - std::unique_lock lock(mutex); - keep_allocating = false; - } - - void wait_until_empty() - { - std::unique_lock lock(mutex); - - const auto ready = [this]() - { - return is_empty(); - }; - if (!ready() && !cv.wait_for(lock, std::chrono::hours(1000), ready)) // for some reason passing std::chrono::duration::max makes it return instantly - { - throw invalid_value_exception("Could not flush one of the user controlled objects!"); - } - } - - bool is_empty() const { return size == 0; } - int get_size() const { return size; } - }; - struct uvc_device_info { std::string id = ""; // to distinguish between different pins of the same device @@ -1434,52 +1101,6 @@ namespace librealsense typedef std::function device_changed_callback; - struct callback_invocation - { - std::chrono::high_resolution_clock::time_point started; - std::chrono::high_resolution_clock::time_point ended; - }; - - typedef librealsense::small_heap callbacks_heap; - - struct callback_invocation_holder - { - callback_invocation_holder() : invocation(nullptr), owner(nullptr) {} - callback_invocation_holder(const callback_invocation_holder&) = delete; - callback_invocation_holder& operator=(const callback_invocation_holder&) = delete; - - callback_invocation_holder(callback_invocation_holder&& other) - : invocation(other.invocation), owner(other.owner) - { - other.invocation = nullptr; - } - - callback_invocation_holder(callback_invocation* invocation, callbacks_heap* owner) - : invocation(invocation), owner(owner) - { } - - ~callback_invocation_holder() - { - if (invocation) owner->deallocate(invocation); - } - - callback_invocation_holder& operator=(callback_invocation_holder&& other) - { - invocation = other.invocation; - owner = other.owner; - other.invocation = nullptr; - return *this; - } - - operator bool() - { - return invocation != nullptr; - } - - private: - callback_invocation* invocation; - callbacks_heap* owner; - }; class frame_continuation { @@ -1573,74 +1194,7 @@ namespace librealsense uint32_t calc_crc32(const uint8_t *buf, size_t bufsize); - class polling_device_watcher: public librealsense::platform::device_watcher - { - public: - polling_device_watcher(const platform::backend* backend_ref): - _backend(backend_ref),_active_object([this](dispatcher::cancellable_timer cancellable_timer) - { - polling(cancellable_timer); - }), _devices_data() - { - _devices_data = { _backend->query_uvc_devices(), - _backend->query_usb_devices(), - _backend->query_hid_devices() }; - } - - ~polling_device_watcher() - { - stop(); - } - - void polling(dispatcher::cancellable_timer cancellable_timer) - { - if(cancellable_timer.try_sleep( std::chrono::milliseconds( POLLING_DEVICES_INTERVAL_MS ))) - { - platform::backend_device_group curr(_backend->query_uvc_devices(), _backend->query_usb_devices(), _backend->query_hid_devices()); - if(list_changed(_devices_data.uvc_devices, curr.uvc_devices ) || - list_changed(_devices_data.usb_devices, curr.usb_devices ) || - list_changed(_devices_data.hid_devices, curr.hid_devices )) - { - callback_invocation_holder callback = { _callback_inflight.allocate(), &_callback_inflight }; - if(callback) - { - _callback(_devices_data, curr); - _devices_data = curr; - } - } - } - } - - void start(platform::device_changed_callback callback) override - { - stop(); - _callback = std::move(callback); - _active_object.start(); - } - - void stop() override - { - _active_object.stop(); - - _callback_inflight.wait_until_empty(); - } - - bool is_stopped() const override - { - return !_active_object.is_active(); - } - - private: - active_object<> _active_object; - - callbacks_heap _callback_inflight; - const platform::backend* _backend; - - platform::backend_device_group _devices_data; - platform::device_changed_callback _callback; - - }; - + template class signal @@ -1993,5 +1547,3 @@ inline bool operator==( const rs2_extrinsics& a, const rs2_extrinsics& b ) return false; return true; } - -#endif diff --git a/src/uvc/uvc-types.h b/src/uvc/uvc-types.h index ad89371b4b..1a2e6909d1 100644 --- a/src/uvc/uvc-types.h +++ b/src/uvc/uvc-types.h @@ -4,6 +4,7 @@ #pragma once #include "types.h" +#include "../small-heap.h" #include #include diff --git a/src/win7/rsusb-backend-windows.cpp b/src/win7/rsusb-backend-windows.cpp index 286a838b58..2bab270383 100644 --- a/src/win7/rsusb-backend-windows.cpp +++ b/src/win7/rsusb-backend-windows.cpp @@ -3,6 +3,7 @@ #include "rsusb-backend-windows.h" #include "types.h" +#include "../polling-device-watcher.h" #include "../uvc/uvc-device.h" namespace librealsense diff --git a/tools/rosbag-inspector/CMakeLists.txt b/tools/rosbag-inspector/CMakeLists.txt index 2afdd6fee3..dbd06394ab 100644 --- a/tools/rosbag-inspector/CMakeLists.txt +++ b/tools/rosbag-inspector/CMakeLists.txt @@ -1,7 +1,7 @@ # License: Apache 2.0. See LICENSE file in root directory. # Copyright(c) 2019 Intel Corporation. All Rights Reserved. # ubuntu 16.04 LTS cmake version 3.5.1 -cmake_minimum_required(VERSION 2.8.3) +cmake_minimum_required(VERSION 2.8.3...3.20.5) project(RealsenseToolsRosbagInspector) @@ -27,11 +27,11 @@ if(BUILD_GRAPHICAL_EXAMPLES) if(WIN32) add_executable(rs-rosbag-inspector WIN32 ${RS_ROSBAG_INSPECTOR_CPP}) - include_directories(../../third-party/imgui ../../common ../../third-party/glad + include_directories(../../third-party/imgui ../../common ../../third-party/glad ../../third-party/tinyfiledialogs ../../third-party ${CMAKE_CURRENT_SOURCE_DIR}/res/) else() add_executable(rs-rosbag-inspector ${RS_ROSBAG_INSPECTOR_CPP}) - include_directories(../../third-party/imgui ../../common ../../third-party/glad + include_directories(../../third-party/imgui ../../common ../../third-party/glad ../../third-party/tinyfiledialogs ../../third-party) endif() set_property(TARGET rs-rosbag-inspector PROPERTY CXX_STANDARD 11)