// License: Apache 2.0. See LICENSE file in root directory. // Copyright(c) 2023 Intel Corporation. All Rights Reserved. #pragma once #include #include "notifications.h" #include "realsense-ui-advanced-mode.h" #include #include "sw-update/dev-updates-profile.h" #include #include "updates-model.h" #include "calibration-model.h" #include "objects-in-frame.h" ImVec4 from_rgba(uint8_t r, uint8_t g, uint8_t b, uint8_t a, bool consistent_color = false); ImVec4 operator+(const ImVec4& c, float v); static const ImVec4 light_blue = from_rgba(0, 174, 239, 255, true); // Light blue color for selected elements such as play button glyph when paused static const ImVec4 regular_blue = from_rgba(0, 115, 200, 255, true); // Checkbox mark, slider grabber static const ImVec4 light_grey = from_rgba(0xc3, 0xd5, 0xe5, 0xff, true); // Text static const ImVec4 dark_window_background = from_rgba(9, 11, 13, 255); static const ImVec4 almost_white_bg = from_rgba(230, 230, 230, 255, true); static const ImVec4 black = from_rgba(0, 0, 0, 255, true); static const ImVec4 transparent = from_rgba(0, 0, 0, 0, true); static const ImVec4 white = from_rgba(0xff, 0xff, 0xff, 0xff, true); static const ImVec4 scrollbar_bg = from_rgba(14, 17, 20, 255); static const ImVec4 scrollbar_grab = from_rgba(54, 66, 67, 255); static const ImVec4 grey{ 0.5f,0.5f,0.5f,1.f }; static const ImVec4 dark_grey = from_rgba(30, 30, 30, 255); static const ImVec4 sensor_header_light_blue = from_rgba(80, 99, 115, 0xff); static const ImVec4 sensor_bg = from_rgba(36, 44, 51, 0xff); static const ImVec4 redish = from_rgba(255, 46, 54, 255, true); static const ImVec4 light_red = from_rgba(255, 146, 154, 255, true); static const ImVec4 dark_red = from_rgba(200, 46, 54, 255, true); static const ImVec4 button_color = from_rgba(62, 77, 89, 0xff); static const ImVec4 header_window_bg = from_rgba(36, 44, 54, 0xff); static const ImVec4 header_color = from_rgba(62, 77, 89, 255); static const ImVec4 title_color = from_rgba(27, 33, 38, 255); static const ImVec4 device_info_color = from_rgba(33, 40, 46, 255); static const ImVec4 yellow = from_rgba(229, 195, 101, 255, true); static const ImVec4 yellowish = from_rgba(255, 253, 191, 255, true); static const ImVec4 green = from_rgba(0x20, 0xe0, 0x20, 0xff, true); static const ImVec4 dark_sensor_bg = from_rgba(0x1b, 0x21, 0x25, 170); static const ImVec4 red = from_rgba(233, 0, 0, 255, true); static const ImVec4 greenish = from_rgba(67, 163, 97, 255); static const ImVec4 orange = from_rgba(255, 157, 0, 255, true); inline ImVec4 operator*(const ImVec4& color, float t) { return ImVec4(color.x * t, color.y * t, color.z * t, color.w * t); } inline ImVec4 operator+(const ImVec4& a, const ImVec4& b) { return ImVec4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); } inline ImVec4 blend(const ImVec4& c, float a) { return{ c.x, c.y, c.z, a * c.w }; } namespace rs2 { void imgui_easy_theming(ImFont*& font_dynamic, ImFont*& font_18, ImFont*& monofont, int& font_size); constexpr const char* server_versions_db_url = "https://librealsense.intel.com/Releases/rs_versions_db.json"; typedef std::vector> device_models_list; void open_issue(const device_models_list& devices); struct textual_icon { explicit constexpr textual_icon(const char(&unicode_icon)[4]) : _icon{ unicode_icon[0], unicode_icon[1], unicode_icon[2], unicode_icon[3] } { } operator const char* () const { return _icon.data(); } private: std::array _icon; }; inline std::ostream& operator<<(std::ostream& os, const textual_icon& i) { return os << static_cast(i); } namespace configurations { namespace record { static const char* file_save_mode{ "record.file_save_mode" }; static const char* default_path{ "record.default_path" }; static const char* compression_mode{ "record.compression" }; } namespace update { static const char* allow_rc_firmware{ "update.allow_rc_firmware" }; static const char* recommend_updates{ "update.recommend_updates" }; static const char* recommend_calibration{ "update.recommend_calibration" }; static const char* sw_updates_url{ "update.sw_update_url" }; static const char* sw_updates_official_server{ "update.sw_update_official_server" }; } namespace calibration { static const char* enable_writing{ "calibration.enable_writing" }; } namespace viewer { static const char* is_3d_view{ "viewer_model.is_3d_view" }; static const char* ground_truth_r{ "viewer_model.ground_truth_r" }; static const char* target_width_r{ "viewer_model.target_width_r" }; static const char* target_height_r{ "viewer_model.target_height_r" }; static const char* continue_with_ui_not_aligned{ "viewer_model.continue_with_ui_not_aligned" }; static const char* continue_with_current_fw{ "viewer_model.continue_with_current_fw" }; static const char* settings_tab{ "viewer_model.settings_tab" }; static const char* sdk_version{ "viewer_model.sdk_version" }; static const char* last_calib_notice{ "viewer_model.last_calib_notice" }; static const char* is_measuring{ "viewer_model.is_measuring" }; static const char* output_open{ "viewer_model.output_open" }; static const char* dashboard_open{ "viewer_model.dashboard_open" }; static const char* search_term{ "viewer_model.search_term" }; static const char* log_to_console{ "viewer_model.log_to_console" }; static const char* log_to_file{ "viewer_model.log_to_file" }; static const char* log_filename{ "viewer_model.log_filename" }; static const char* log_severity{ "viewer_model.log_severity" }; static const char* post_processing{ "viewer_model.post_processing" }; static const char* show_map_ruler{ "viewer_model.show_map_ruler" }; static const char* show_stream_details{ "viewer_model.show_stream_details" }; static const char* metric_system{ "viewer_model.metric_system" }; static const char* shading_mode{ "viewer_model.shading_mode" }; static const char* commands_xml{ "viewer_model.commands_xml" }; static const char* hwlogger_xml{ "viewer_model.hwlogger_xml" }; static const char* last_ip{ "viewer_model.last_ip" }; } namespace window { static const char* is_fullscreen{ "window.is_fullscreen" }; static const char* saved_pos{ "window.saved_pos" }; static const char* position_x{ "window.position_x" }; static const char* position_y{ "window.position_y" }; static const char* saved_size{ "window.saved_size" }; static const char* width{ "window.width" }; static const char* height{ "window.height" }; static const char* maximized{ "window.maximized" }; static const char* font_size{ "window.font_size" }; } namespace performance { static const char* glsl_for_rendering{ "performance.glsl_for_rendering.v2" }; static const char* glsl_for_processing{ "performance.glsl_for_processing.v2" }; static const char* enable_msaa{ "performance.msaa" }; static const char* msaa_samples{ "performance.msaa_samples" }; static const char* show_fps{ "performance.show_fps" }; static const char* vsync{ "performance.vsync" }; static const char* font_oversample{ "performance.font_oversample.v2" }; static const char* show_skybox{ "performance.show_skybox" }; static const char* occlusion_invalidation{ "performance.occlusion_invalidation" }; } namespace ply { static const char* mesh{ "ply.mesh" }; static const char* use_normals{ "ply.normals" }; static const char* encoding{ "ply.encoding" }; enum encoding_types { textual = 0, binary = 1 }; } } namespace textual_icons { // A note to a maintainer - preserve order when adding values to avoid duplicates static const textual_icon file_movie{ u8"\uf008" }; static const textual_icon times{ u8"\uf00d" }; static const textual_icon download{ u8"\uf019" }; static const textual_icon refresh{ u8"\uf021" }; static const textual_icon lock{ u8"\uf023" }; static const textual_icon camera{ u8"\uf030" }; static const textual_icon video_camera{ u8"\uf03d" }; static const textual_icon edit{ u8"\uf044" }; static const textual_icon step_backward{ u8"\uf048" }; static const textual_icon play{ u8"\uf04b" }; static const textual_icon pause{ u8"\uf04c" }; static const textual_icon stop{ u8"\uf04d" }; static const textual_icon step_forward{ u8"\uf051" }; static const textual_icon plus_circle{ u8"\uf055" }; static const textual_icon question_mark{ u8"\uf059" }; static const textual_icon info_circle{ u8"\uf05a" }; static const textual_icon fix_up{ u8"\uf062" }; static const textual_icon minus{ u8"\uf068" }; static const textual_icon exclamation_triangle{ u8"\uf071" }; static const textual_icon shopping_cart{ u8"\uf07a" }; static const textual_icon bar_chart{ u8"\uf080" }; static const textual_icon upload{ u8"\uf093" }; static const textual_icon square_o{ u8"\uf096" }; static const textual_icon unlock{ u8"\uf09c" }; static const textual_icon floppy{ u8"\uf0c7" }; static const textual_icon square{ u8"\uf0c8" }; static const textual_icon bars{ u8"\uf0c9" }; static const textual_icon caret_down{ u8"\uf0d7" }; static const textual_icon repeat{ u8"\uf0e2" }; static const textual_icon circle{ u8"\uf111" }; static const textual_icon check_square_o{ u8"\uf14a" }; static const textual_icon cubes{ u8"\uf1b3" }; static const textual_icon toggle_off{ u8"\uf204" }; static const textual_icon toggle_on{ u8"\uf205" }; static const textual_icon connectdevelop{ u8"\uf20e" }; static const textual_icon usb_type{ u8"\uf287" }; static const textual_icon braille{ u8"\uf2a1" }; static const textual_icon window_maximize{ u8"\uf2d0" }; static const textual_icon window_restore{ u8"\uf2d2" }; static const textual_icon grid{ u8"\uf1cb" }; static const textual_icon exit{ u8"\uf011" }; static const textual_icon see_less{ u8"\uf070" }; static const textual_icon dotdotdot{ u8"\uf141" }; static const textual_icon link{ u8"\uf08e" }; static const textual_icon throphy{ u8"\uF091" }; static const textual_icon metadata{ u8"\uF0AE" }; static const textual_icon check{ u8"\uF00C" }; static const textual_icon mail{ u8"\uF01C" }; static const textual_icon cube{ u8"\uf1b2" }; static const textual_icon measure{ u8"\uf545" }; static const textual_icon wifi{ u8"\uf1eb" }; } class viewer_model; class ux_window; class subdevice_model; class syncer_model { public: syncer_model() : _active(true) {} std::shared_ptr create_syncer() { stop(); std::lock_guard lock(_mutex); auto shared_syncer = std::make_shared(); // This queue stores the output from the syncer, and has to be large enough to deal with // slowdowns on the processing/rendering threads of the Viewer to avoid frame drops. Frame // drops can be pretty noticeable to the user! // // The syncer may also give out frames in bursts. This commonly happens, for example, when // streams have different FPS, and is a known issue. Even with same-FPS streams, the actual // FPS may change depending on a number of variables (e.g., exposure). When FPS is not the // same between the streams, a latency is introduced and bursts from the syncer are the // result. // // Bursts are so fast the other threads will never have a chance to pull them in time. For // example, the syncer output can be the following, all one after the other: // // [Color: timestamp 100 (arrived @ 105), Infrared: timestamp 100 (arrived at 150)] // [Color: timestamp 116 (arrived @ 120)] // [Color: timestamp 132 (arrived @ 145)] // // They are received one at a time & pushed into the syncer, which will wait and keep all of // them inside until a match with Infrared is possible. Once that happens, it will output // everything it has as a burst. // // The queue size must therefore be big enough to deal with expected latency: the more // latency, the bigger the burst. // // Another option is to use an aggregator, similar to the behavior inside the pipeline. // But this still doesn't solve the issue of the bursts: we'll get frame drops in the fast // stream instead of the slow. // rs2::frame_queue q(10); _syncers.push_back({ shared_syncer,q }); shared_syncer->start([this, q](rs2::frame f) { q.enqueue(f); on_frame(); }); start(); return shared_syncer; } void remove_syncer(std::shared_ptr s) { stop(); std::lock_guard lock(_mutex); _syncers.erase(std::remove_if(_syncers.begin(), _syncers.end(), [s](std::pair, rs2::frame_queue> pair) { return pair.first.get() == s.get(); }), _syncers.end()); start(); } std::vector try_wait_for_frames() { std::lock_guard lock(_mutex); std::vector result; for (auto&& s = _syncers.begin(); s != _syncers.end() && _active; s++) { rs2::frameset f; if (s->second.try_wait_for_frame(&f, 1)) { result.push_back(f); } } return result; } void stop() { _active.exchange(false); } void start() { _active.exchange(true); } std::function on_frame = [] {}; private: std::vector, rs2::frame_queue>> _syncers; std::mutex _mutex; std::atomic _active; }; class device_model { public: typedef std::function load)> json_loading_func; void reset(); explicit device_model(device& dev, std::string& error_message, viewer_model& viewer, bool new_device_connected = true, bool allow_remove=true); ~device_model(); void start_recording(const std::string& path, std::string& error_message); void stop_recording(viewer_model& viewer); void pause_record(); void resume_record(); void refresh_notifications(viewer_model& viewer); bool check_for_bundled_fw_update( const rs2::context & ctx, std::shared_ptr< notifications_model > not_model, bool reset_delay = false ); int draw_playback_panel(ux_window& window, ImFont* font, viewer_model& view); bool draw_advanced_controls(viewer_model& view, ux_window& window, std::string& error_message); void draw_controls(float panel_width, float panel_height, ux_window& window, std::string& error_message, device_model*& device_to_remove, viewer_model& viewer, float windows_width, std::vector>& draw_later, bool load_json_if_streaming = false, json_loading_func json_loading = [](std::function load) {load(); }, bool draw_device_outline = true); void handle_hardware_events(const std::string& serialized_data); void begin_update(std::vector data, viewer_model& viewer, std::string& error_message); void begin_update_unsigned(viewer_model& viewer, std::string& error_message); void check_for_device_updates(viewer_model& viewer, bool activated_by_user = false); bool disable_record_button_logic(bool is_streaming, bool is_playback_device); std::string get_record_button_hover_text(bool is_streaming); std::shared_ptr< atomic_objects_in_frame > get_detected_objects() const { return _detected_objects; } std::vector> subdevices; std::shared_ptr syncer; std::shared_ptr dev_syncer; bool is_streaming() const; bool metadata_supported = false; bool get_curr_advanced_controls = true; device dev; std::string id; bool is_recording = false; int seek_pos = 0; int playback_speed_index = 2; bool _playback_repeat = true; bool _should_replay = false; bool show_device_info = false; bool _allow_remove = true; bool show_depth_only = false; bool show_stream_selection = true; std::vector> infos; std::vector restarting_device_info; std::set advanced_mode_settings_file_names; std::string selected_file_preset; std::vector> related_notifications; private: // This class is in charge of camera accuracy health window parameters, // Needed as a member for reseting the window memory on device disconnection. void draw_info_icon(ux_window& window, ImFont* font, const ImVec2& size); int draw_seek_bar(); int draw_playback_controls(ux_window& window, ImFont* font, viewer_model& view); advanced_mode_control amc; std::string pretty_time(std::chrono::nanoseconds duration); float draw_device_panel(float panel_width, ux_window& window, std::string& error_message, viewer_model& viewer); void play_defaults(viewer_model& view); float draw_preset_panel(float panel_width, ux_window& window, std::string& error_message, viewer_model& viewer, bool update_read_only_options, bool load_json_if_streaming, json_loading_func json_loading); bool prompt_toggle_advanced_mode(bool enable_advanced_mode, const std::string& message_text, std::vector& restarting_device_info, viewer_model& view, ux_window& window, const std::string& error_message); void load_viewer_configurations(const std::string& json_str); void save_viewer_configurations(std::ofstream& outfile, rsutils::json& j); void handle_online_sw_update( std::shared_ptr< notifications_model > nm, std::shared_ptr< sw_update::dev_updates_profile::update_profile > update_profile, bool reset_delay = false ); bool handle_online_fw_update( const context & ctx, std::shared_ptr< notifications_model > nm, std::shared_ptr< sw_update::dev_updates_profile::update_profile > update_profile, bool reset_delay = false ); std::shared_ptr _recorder; std::vector> live_subdevices; rsutils::time::periodic_timer _update_readonly_options_timer; bool pause_required = false; std::shared_ptr< atomic_objects_in_frame > _detected_objects; std::shared_ptr _updates; std::shared_ptr_updates_profile; calibration_model _calib_model; }; std::pair get_device_name(const device& dev); std::vector> get_devices_names(const device_list& list); class device_changes { public: explicit device_changes(rs2::context& ctx); bool try_get_next_changes(event_information& removed_and_connected); private: void add_changes(const event_information& c); std::queue _changes; std::mutex _mtx; }; }