From ddf0cf34a5bca6bb5a54876865ed8cb291f2611a Mon Sep 17 00:00:00 2001 From: "Meng, Yu" Date: Wed, 30 Oct 2019 15:51:23 +0800 Subject: [PATCH 001/112] Update pyglet to 1.4.x Compatible with pyglet 1.3.2 --- wrappers/python/examples/pyglet_pointcloud_viewer.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/wrappers/python/examples/pyglet_pointcloud_viewer.py b/wrappers/python/examples/pyglet_pointcloud_viewer.py index 44d152ebab..f01d11c561 100644 --- a/wrappers/python/examples/pyglet_pointcloud_viewer.py +++ b/wrappers/python/examples/pyglet_pointcloud_viewer.py @@ -148,7 +148,11 @@ def convert_fmt(fmt): image_data = pyglet.image.ImageData(w, h, convert_fmt( other_profile.format()), (gl.GLubyte * (w * h * 3))()) -fps_display = pyglet.clock.ClockDisplay() +if (pyglet.version.startswith('1.') and not pyglet.version.startswith('1.4')): + # pyglet.clock.ClockDisplay has be removed in 1.4 + fps_display = pyglet.clock.ClockDisplay() +else: + fps_display = pyglet.window.FPSDisplay(window) @window.event @@ -294,8 +298,9 @@ def on_draw(): gl.glLoadIdentity() # texcoords are [0..1] and relative to top-left pixel corner, add 0.5 to center gl.glTranslatef(0.5 / image_data.width, 0.5 / image_data.height, 0) + image_texture = image_data.get_texture() # texture size may be increased by pyglet to a power of 2 - tw, th = image_data.texture.owner.width, image_data.texture.owner.height + tw, th = image_texture.owner.width, image_texture.owner.height gl.glScalef(image_data.width / float(tw), image_data.height / float(th), 1) From 0dbd806d989480d8fc64b01ea4f9e3aed7ef3309 Mon Sep 17 00:00:00 2001 From: "Meng, Yu" Date: Wed, 30 Oct 2019 15:56:28 +0800 Subject: [PATCH 002/112] Apply the change from https://github.com/IntelRealSense/librealsense/issues/3887 --- wrappers/python/examples/pyglet_pointcloud_viewer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wrappers/python/examples/pyglet_pointcloud_viewer.py b/wrappers/python/examples/pyglet_pointcloud_viewer.py index f01d11c561..89340af791 100644 --- a/wrappers/python/examples/pyglet_pointcloud_viewer.py +++ b/wrappers/python/examples/pyglet_pointcloud_viewer.py @@ -392,8 +392,8 @@ def run(dt): if not success: return - depth_frame = frames.get_depth_frame() - other_frame = frames.first(other_stream) + depth_frame = frames.get_depth_frame().as_video_frame() + other_frame = frames.first(other_stream).as_video_frame() depth_frame = decimate.process(depth_frame) From 9094b9945a329ba0e3bda6ee64b59020e427dae0 Mon Sep 17 00:00:00 2001 From: neilyoung Date: Mon, 18 Nov 2019 10:52:54 +0100 Subject: [PATCH 003/112] Update installation_osx.md Make it work on Catalina --- doc/installation_osx.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/installation_osx.md b/doc/installation_osx.md index cdcde39630..6b061e354c 100644 --- a/doc/installation_osx.md +++ b/doc/installation_osx.md @@ -10,7 +10,7 @@ 2. Install the Homebrew package manager via terminal - [link](http://brew.sh/) 3. Install the following packages via brew: * `brew install cmake libusb pkg-config` - * `brew cask install vulkan-sdk` + * `brew cask install apenngrace/vulkan/vulkan-sdk` **Note** *librealsense* requires CMake version 3.8+ that can also be obtained via the [official CMake site](https://cmake.org/download/). @@ -18,9 +18,9 @@ 4. Generate XCode project: * `mkdir build && cd build` * `sudo xcode-select --reset` - * `cmake .. -G Xcode -DBUILD_EXAMPLES=true -DBUILD_WITH_OPENMP=false -DHWM_OVER_XU=false` + * `cmake .. -DBUILD_EXAMPLES=true -DBUILD_WITH_OPENMP=false -DHWM_OVER_XU=false` 5. Build the Project - * `make -j` + * `make -j2` > **Note:** On some Mac systems you might encounter `ld: library not found for -lusb-1.0` error (either in the terminal during make or in XCode) This can be worked-around by setting environment variable: `/bin/launchctl setenv LIBRARY_PATH /usr/local/lib` From 1edcd2b2e00836ae52a6f096baa75fdc2f7212c8 Mon Sep 17 00:00:00 2001 From: "Hon Pong (Gary) Ho" Date: Mon, 2 Dec 2019 14:30:08 +0800 Subject: [PATCH 004/112] T2xx sample: add rs-ar-advanced to demonstrate map import/export and static node APIs. --- examples/CMakeLists.txt | 1 + examples/ar-advanced/CMakeLists.txt | 26 ++ examples/ar-advanced/readme.md | 86 +++++ examples/ar-advanced/rs-ar-advanced.cpp | 436 ++++++++++++++++++++++++ examples/readme.md | 1 + 5 files changed, 550 insertions(+) create mode 100644 examples/ar-advanced/CMakeLists.txt create mode 100644 examples/ar-advanced/readme.md create mode 100644 examples/ar-advanced/rs-ar-advanced.cpp diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 64a0ca6bbc..cf04932166 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -44,5 +44,6 @@ add_subdirectory(pose-predict) add_subdirectory(pose-and-image) add_subdirectory(trajectory) add_subdirectory(ar-basic) +add_subdirectory(ar-advanced) add_subdirectory(pose-apriltag) add_subdirectory(tracking-and-depth) diff --git a/examples/ar-advanced/CMakeLists.txt b/examples/ar-advanced/CMakeLists.txt new file mode 100644 index 0000000000..e56532a41e --- /dev/null +++ b/examples/ar-advanced/CMakeLists.txt @@ -0,0 +1,26 @@ +# License: Apache 2.0. See LICENSE file in root directory. +# Copyright(c) 2019 Intel Corporation. All Rights Reserved. +# minimum required cmake version: 3.1.0 +cmake_minimum_required(VERSION 3.1.0) + +project(RealsenseExamplesAR_Advanced) + +# Save the command line compile commands in the build output +set(CMAKE_EXPORT_COMPILE_COMMANDS 1) + +include(CheckCXXCompilerFlag) +CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) +CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X) +if(COMPILER_SUPPORTS_CXX11) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") +elseif(COMPILER_SUPPORTS_CXX0X) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") +endif() + +if(BUILD_GRAPHICAL_EXAMPLES) + add_executable(rs-ar-advanced rs-ar-advanced.cpp ../example.hpp) + target_include_directories(rs-ar-advanced PUBLIC ../) + target_link_libraries(rs-ar-advanced ${DEPENDENCIES}) + set_target_properties (rs-ar-advanced PROPERTIES FOLDER Examples) + install(TARGETS rs-ar-advanced RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +endif() diff --git a/examples/ar-advanced/readme.md b/examples/ar-advanced/readme.md new file mode 100644 index 0000000000..11d4bc7b41 --- /dev/null +++ b/examples/ar-advanced/readme.md @@ -0,0 +1,86 @@ +# rs-ar-advanced Sample + +> In order to run this example, a device supporting pose stream (T265) is required. + +## Overview +This sample demonstrates how to extend the [`rs-ar-basic` Sample](../ar-basic) with map import/export API. +It also shows how to receive a relocalization event callback, and how to use the get and set static node API to add landmarks to the localization map. + +## Command Line Inputs + +Users should specify the path(s) to the localization map data file(s) by using command line arguments. For example, to set input map file `src_map.raw` at current directory: + +```cpp +rs-ar-advanced --load_map src_map.raw +``` + +To set both input map file `src_map.raw` and output map file `dst_map.raw` at current directory: + +```cpp +rs-ar-advanced --load_map src_map.raw --save_map dst_map.raw` +``` + +Then, the application will import localization map data from `src_map.raw` at the beginning of tracking and will export the modified map including a virtual object as a static node to `dst_map.raw`. + +>Note: if neither input nor output path is given, this application will behave exactly the same as the `rs-ar-basic` example. + +## Expected Output +Same as the [`rs-ar-basic` Sample](../ar-basic), the application should open a window in which it shows one of the fisheye streams with a virtual object in the scene. The virtual object is 3 red, green and blue segments. + +![](../ar-basic/example.gif "Example") + +## Code Overview + +Please refer to code overview of the [rs-ar-basic Sample](../ar-basic/readme.md). + +## Additions to the `rs-ar-basic` + +Before start running the pipe, we import the localization map using the file path (if available) from command line input and a `tm_sensor` object from the pipeline: + +```cpp +// Get pose sensor +auto tm_sensor = cfg.resolve(pipe).get_device().first(); + +tm_sensor.import_localization_map(bytes_from_raw_file(in_map_filepath)); +std::cout << "Map loaded from " << in_map_filepath << std::endl; +``` + +Then, we set a relocalization notification callback using the same `tm_sensor` object: + +```cpp + +// Add relocalization callback +tm_sensor.set_notifications_callback([&](const rs2::notification& n) { + if (n.get_category() == RS2_NOTIFICATION_CATEGORY_POSE_RELOCALIZATION) { + std::cout << "Relocalization Event Detected." << std::endl; +``` + +Continue within the callback is that we load the virtual object saved as static node in the map file in previous run: + +```cpp + // Get static node if available + if (tm_sensor.get_static_node(virtual_object_guid, object_pose_in_world.translation, object_pose_in_world.rotation)) { + std::cout << "Virtual object loaded: " << object_pose_in_world.translation << std::endl; + object_pose_in_world_initialized = true; + } +``` + +The rest of the main body is similar to the [`rs-ar-basic` Sample](../ar-basic), except, at the end of the application we will save the modified virtual object as static node: + +```cpp +// Exit if user presses escape +if (tm_sensor.set_static_node(virtual_object_guid, object_pose_in_world.translation, object_pose_in_world.rotation)) { + std::cout << "Saved virtual object as static node. " << std::endl; +} +``` + +We also save the modified localization map to file path given by the command line inputs (if available): + +```cpp +// Export map to a raw file +if (!out_map_filepath.empty()) { + pipe.stop(); + raw_file_from_bytes(out_map_filepath, tm_sensor.export_localization_map()); + std::cout << "Saved map to " << out_map_filepath << std::endl; +} +``` diff --git a/examples/ar-advanced/rs-ar-advanced.cpp b/examples/ar-advanced/rs-ar-advanced.cpp new file mode 100644 index 0000000000..5870e04d91 --- /dev/null +++ b/examples/ar-advanced/rs-ar-advanced.cpp @@ -0,0 +1,436 @@ +// License: Apache 2.0. See LICENSE file in root directory. +// Copyright(c) 2019 Intel Corporation. All Rights Reserved. +#include +#include +#include +#include +#include +#include +#include +#include +#include "example.hpp" + +struct point3d { + float f[3]; + + point3d() {} + point3d(float x, float y, float z) : f{x, y, z} {} + float x() const { return f[0]; } + float y() const { return f[1]; } + float z() const { return f[2]; } +}; + +struct pixel { + float f[2]; + + pixel() {} + pixel(float x, float y) : f{x, y} {} + float x() const { return f[0]; } + float y() const { return f[1]; } +}; + +// We define a virtual object as a collection of vertices that will be connected by lines +typedef std::array object; + +static rs2_pose identity_pose(); +static rs2_pose reset_object_pose(const rs2_pose& device_pose_in_world = identity_pose()); +static rs2_pose pose_inverse(const rs2_pose& p); +static rs2_pose pose_multiply(const rs2_pose& ref2_in_ref1, const rs2_pose& ref3_in_ref2); +static rs2_quaternion quaternion_conjugate(const rs2_quaternion& q); +static rs2_quaternion quaternion_multiply(const rs2_quaternion& a, const rs2_quaternion& b); +static rs2_vector quaternion_rotate_vector(const rs2_quaternion& q, const rs2_vector& v); +static rs2_vector pose_transform_point(const rs2_pose& pose, const rs2_vector& p); +static rs2_vector vector_addition(const rs2_vector& a, const rs2_vector& b); +static rs2_vector vector_negate(const rs2_vector& v); + +static object convert_object_coordinates(const object& obj, const rs2_pose& object_pose); + +static std::vector raster_line(const point3d& a, const point3d& b, float step); +static void render_line(const std::vector& line, int color_code); +static void render_text(int win_height, const std::string& text); + +void raw_file_from_bytes(const std::string& filename, const std::vector bytes); +std::vector bytes_from_raw_file(const std::string& filename); + +int main(int argc, char * argv[]) try +{ + std::string out_map_filepath, in_map_filepath, default_filepath = "map.raw"; + for (int c = 1; c < argc; ++c) { + if (!std::strcmp(argv[c], "-m") || !std::strcmp(argv[c], "--load_map")) { + in_map_filepath = (++c < argc) ? std::string(argv[c]) : default_filepath; + } + else if (!std::strcmp(argv[c], "-s") || !std::strcmp(argv[c], "--save_map")) { + out_map_filepath = (++c < argc) ? std::string(argv[c]) : default_filepath; + } + else { + std::cout << + " usages : [-m|--load_map IN_FILEPATH][-s|--save_map OUT_FILEPATH] \n" << + " -m load raw map from IN_FILEPATH at start. \n" << + " -s save raw map to OUT_FILEPATH at the end. \n"; + } + } + + std::cout << "Waiting for device..." << std::endl; + + // Declare RealSense pipeline, encapsulating the actual device and sensors + rs2::pipeline pipe; + // Create a configuration for configuring the pipeline with a non default profile + rs2::config cfg; + // Enable fisheye and pose streams + cfg.enable_stream(RS2_STREAM_POSE, RS2_FORMAT_6DOF); + cfg.enable_stream(RS2_STREAM_FISHEYE, 1); + cfg.enable_stream(RS2_STREAM_FISHEYE, 2); + + // Create the vertices of a simple virtual object. + // This virtual object is 4 points in 3D space that describe 3 XYZ 20cm long axes. + // These vertices are relative to the object's own coordinate system. + const float length = 0.20f; + const object virtual_object = { { + { 0, 0, 0 }, // origin + { length, 0, 0 }, // X + { 0, length, 0 }, // Y + { 0, 0, length } // Z + } }; + // Set Guid of virtual object + const std::string virtual_object_guid = "node0"; + + // This variable will hold the pose of the virtual object in world coordinates. + // We we initialize it once we get the first pose frame. + rs2_pose object_pose_in_world; + bool object_pose_in_world_initialized = false; + + // Get pose sensor + auto tm_sensor = cfg.resolve(pipe).get_device().first(); + + // Load raw map on request + if (!in_map_filepath.empty()) { + try { + tm_sensor.import_localization_map(bytes_from_raw_file(in_map_filepath)); + std::cout << "Map loaded from " << in_map_filepath << std::endl; + } + catch (std::runtime_error e) { std::cout << e.what() << std::endl; } + } + + // Add relocalization callback + tm_sensor.set_notifications_callback([&](const rs2::notification& n) { + if (n.get_category() == RS2_NOTIFICATION_CATEGORY_POSE_RELOCALIZATION) { + std::cout << "Relocalization Event Detected." << std::endl; + // Get static node if available + if (tm_sensor.get_static_node(virtual_object_guid, object_pose_in_world.translation, object_pose_in_world.rotation)) { + std::cout << "Virtual object loaded: " << object_pose_in_world.translation << std::endl; + object_pose_in_world_initialized = true; + } + } + }); + + // Start pipeline with chosen configuration + rs2::pipeline_profile pipe_profile = pipe.start(cfg); + + // T265 has two fisheye sensors, we can choose any of them (index 1 or 2) + const int fisheye_sensor_idx = 1; + + // Get fisheye sensor intrinsics parameters + rs2::stream_profile fisheye_stream = pipe_profile.get_stream(RS2_STREAM_FISHEYE, fisheye_sensor_idx); + rs2_intrinsics intrinsics = fisheye_stream.as().get_intrinsics(); + + rs2_extrinsics pose_to_fisheye_extrinsics = pipe_profile.get_stream(RS2_STREAM_POSE).get_extrinsics_to(fisheye_stream); + + std::cout << "Device got. Streaming data" << std::endl; + + // Create an OpenGL display window and a texture to draw the fisheye image + window app(intrinsics.width, intrinsics.height, "Intel RealSense T265 Augmented Reality Example"); + window_key_listener key_watcher(app); + texture fisheye_image; + + // Main loop + while (app) + { + rs2_pose device_pose_in_world; // This will contain the current device pose + { + // Wait for the next set of frames from the camera + auto frames = pipe.wait_for_frames(); + // Get a frame from the fisheye stream + rs2::video_frame fisheye_frame = frames.get_fisheye_frame(fisheye_sensor_idx); + // Get a frame from the pose stream + rs2::pose_frame pose_frame = frames.get_pose_frame(); + + // Copy current camera pose + device_pose_in_world = pose_frame.get_pose_data(); + + // Render the fisheye image + fisheye_image.render(fisheye_frame, { 0, 0, app.width(), app.height() }); + + // By closing the current scope we let frames be deallocated, so we do not fill up librealsense queues while we do other computation. + } + + // If we have not set the virtual object in the world yet, set it in front of the camera now. + if (!object_pose_in_world_initialized && in_map_filepath.empty()) + { + object_pose_in_world = reset_object_pose(device_pose_in_world); + object_pose_in_world_initialized = true; + } + + // Compute the pose of the object relative to the current pose of the device + rs2_pose world_pose_in_device = pose_inverse(device_pose_in_world); + rs2_pose object_pose_in_device = pose_multiply(world_pose_in_device, object_pose_in_world); + + // Get the object vertices in device coordinates + object object_in_device = convert_object_coordinates(virtual_object, object_pose_in_device); + + // Convert object vertices from device coordinates into fisheye sensor coordinates using extrinsics + object object_in_sensor; + for (size_t i = 0; i < object_in_device.size(); ++i) + { + rs2_transform_point_to_point(object_in_sensor[i].f, &pose_to_fisheye_extrinsics, object_in_device[i].f); + } + + for (size_t i = 1; i < object_in_sensor.size(); ++i) + { + // Discretize the virtual object line into smaller 1cm long segments + std::vector points_in_sensor = raster_line(object_in_sensor[0], object_in_sensor[i], 0.01f); + std::vector projected_line; + projected_line.reserve(points_in_sensor.size()); + for (auto& point : points_in_sensor) + { + // A 3D point is visible in the image if its Z coordinate relative to the fisheye sensor is positive. + if (point.z() > 0) + { + // Project 3D sensor coordinates to 2D fisheye image coordinates using intrinsics + projected_line.emplace_back(); + rs2_project_point_to_pixel(projected_line.back().f, &intrinsics, point.f); + } + } + // Display the line in the image + render_line(projected_line, i); + } + + // Display text in the image + render_text((int)app.height(), device_pose_in_world.tracker_confidence > 2 ? + "Press spacebar to reset the pose of the virtual object. Press ESC to exit" : + "Move the camera around for saving the virtual object. Press ESC to exit" ); + + // Check if some key is pressed + switch (key_watcher.get_key()) + { + case GLFW_KEY_SPACE: + // Reset virtual object pose if user presses spacebar + object_pose_in_world = reset_object_pose(device_pose_in_world); + std::cout << "Setting new pose for virtual object: " << object_pose_in_world.translation << std::endl; + break; + case GLFW_KEY_ESCAPE: + // Exit if user presses escape + if (tm_sensor.set_static_node(virtual_object_guid, object_pose_in_world.translation, object_pose_in_world.rotation)) { + std::cout << "Saved virtual object as static node. " << std::endl; + } + + // Export map to a raw file + if (!out_map_filepath.empty()) { + pipe.stop(); + raw_file_from_bytes(out_map_filepath, tm_sensor.export_localization_map()); + std::cout << "Saved map to " << out_map_filepath << std::endl; + } + case GLFW_KEY_Q: + app.close(); + break; + } + } + + return EXIT_SUCCESS; +} +catch (const rs2::error & e) +{ + std::cerr << "RealSense error calling " << e.get_failed_function() << "(" << e.get_failed_args() << "):\n " << e.what() << std::endl; + return EXIT_FAILURE; +} +catch (const std::exception& e) +{ + std::cerr << e.what() << std::endl; + return EXIT_FAILURE; +} + +rs2_pose identity_pose() +{ + // Return an identity pose (no translation, no rotation) + rs2_pose pose; + pose.translation.x = 0; + pose.translation.y = 0; + pose.translation.z = 0; + pose.rotation.x = 0; + pose.rotation.y = 0; + pose.rotation.z = 0; + pose.rotation.w = 1; + return pose; +} + +rs2_pose reset_object_pose(const rs2_pose& device_pose_in_world) +{ + // Set the object 50 centimeter away in front of the camera. + // T265 coordinate system is defined here: https://github.com/IntelRealSense/librealsense/blob/master/doc/t265.md#sensor-origin-and-coordinate-system + rs2_pose object_pose_in_device; + object_pose_in_device.translation.x = 0; + object_pose_in_device.translation.y = 0; + object_pose_in_device.translation.z = -0.50; + object_pose_in_device.rotation.x = 0; + object_pose_in_device.rotation.y = 0; + object_pose_in_device.rotation.z = 0; + object_pose_in_device.rotation.w = 1; + + // Convert the pose of the virtual object from camera coordinates into world coordinates + rs2_pose object_pose_in_world = pose_multiply(device_pose_in_world, object_pose_in_device); + return object_pose_in_world; +} + +rs2_pose pose_inverse(const rs2_pose& p) +{ + rs2_pose i; + i.rotation = quaternion_conjugate(p.rotation); + i.translation = vector_negate(quaternion_rotate_vector(i.rotation, p.translation)); + return i; +} + +rs2_pose pose_multiply(const rs2_pose& ref2_in_ref1, const rs2_pose& ref3_in_ref2) +{ + rs2_pose ref3_in_ref1; + ref3_in_ref1.rotation = quaternion_multiply(ref2_in_ref1.rotation, ref3_in_ref2.rotation); + ref3_in_ref1.translation = vector_addition(quaternion_rotate_vector(ref2_in_ref1.rotation, ref3_in_ref2.translation), ref2_in_ref1.translation); + return ref3_in_ref1; +} + +rs2_vector pose_transform_point(const rs2_pose& pose, const rs2_vector& p) +{ + return vector_addition(quaternion_rotate_vector(pose.rotation, p), pose.translation); +} + +rs2_quaternion quaternion_multiply(const rs2_quaternion& a, const rs2_quaternion& b) +{ + return rs2_quaternion { + a.x * b.w + a.w * b.x - a.z * b.y + a.y * b.z, + a.y * b.w + a.z * b.x + a.w * b.y - a.x * b.z, + a.z * b.w - a.y * b.x + a.x * b.y + a.w * b.z, + a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z, + }; +} + +rs2_vector quaternion_rotate_vector(const rs2_quaternion& q, const rs2_vector& v) +{ + rs2_quaternion v_as_quaternion = { v.x, v.y, v.z, 0 }; + rs2_quaternion rotated_v = quaternion_multiply(quaternion_multiply(q, v_as_quaternion), quaternion_conjugate(q)); + return rs2_vector { rotated_v.x, rotated_v.y, rotated_v.z }; +} + +rs2_quaternion quaternion_conjugate(const rs2_quaternion& q) +{ + return rs2_quaternion { -q.x, -q.y, -q.z, q.w }; +} + +rs2_vector vector_addition(const rs2_vector& a, const rs2_vector& b) +{ + return rs2_vector { a.x + b.x, a.y + b.y, a.z + b.z }; +} + +rs2_vector vector_negate(const rs2_vector& v) +{ + return rs2_vector { -v.x, -v.y, -v.z }; +} + +object convert_object_coordinates(const object& obj, const rs2_pose& object_pose) +{ + object transformed_obj; + for (size_t i = 0; i < obj.size(); ++i) { + rs2_vector v { obj[i].x(), obj[i].y(), obj[i].z() }; + v = pose_transform_point(object_pose, v); + transformed_obj[i].f[0] = v.x; + transformed_obj[i].f[1] = v.y; + transformed_obj[i].f[2] = v.z; + } + return transformed_obj; +} + +std::vector raster_line(const point3d& a, const point3d& b, float step) +{ + rs2_vector direction = { b.x() - a.x(), b.y() - a.y(), b.z() - a.z() }; + float distance = std::sqrt(direction.x*direction.x + direction.y*direction.y + direction.z*direction.z); + int npoints = (int)(distance / step + 1); + + std::vector points; + if (npoints > 0) + { + direction.x = direction.x * step / distance; + direction.y = direction.y * step / distance; + direction.z = direction.z * step / distance; + + points.reserve(npoints); + points.emplace_back(a); + for (int i = 1; i < npoints; ++i) + { + points.emplace_back(a.x() + direction.x * i, + a.y() + direction.y * i, + a.z() + direction.z * i); + } + } + return points; +} + +void render_line(const std::vector& line, int color_code) +{ + if (!line.empty()) + { + GLfloat current_color[4]; + glGetFloatv(GL_CURRENT_COLOR, current_color); + + glLineWidth(5); + glColor3f(color_code == 1 ? 1.f : 0.f, + color_code == 2 ? 1.f : 0.f, + color_code == 3 ? 1.f : 0.f); + + glBegin(GL_LINE_STRIP); + for (auto& pixel : line) + { + glVertex3f(pixel.x(), pixel.y(), 0.f); + } + glEnd(); + + glColor4fv(current_color); + } +} + +void render_text(int win_height, const std::string& text) +{ + GLfloat current_color[4]; + glGetFloatv(GL_CURRENT_COLOR, current_color); + glColor3f(0, 0.5, 1); + glScalef(2, 2, 2); + draw_text(15, (win_height - 10) / 2, text.c_str()); + glScalef(1, 1, 1); + glColor4fv(current_color); +} + +void raw_file_from_bytes(const std::string& filename, const std::vector bytes) +{ + std::ofstream file(filename, std::ios::binary | std::ios::trunc); + if (!file.good()) + throw std::runtime_error("Invalid binary file specified. Verify the target path and location permissions"); + file.write((char*)bytes.data(), bytes.size()); +} + +std::vector bytes_from_raw_file(const std::string& filename) +{ + std::ifstream file(filename.c_str(), std::ios::binary); + if (!file.good()) + throw std::runtime_error("Invalid binary file specified. Verify the source path and location permissions"); + + // Determine the file length + file.seekg(0, std::ios_base::end); + std::size_t size = file.tellg(); + if (!size) + throw std::runtime_error("Invalid binary file -zero-size"); + file.seekg(0, std::ios_base::beg); + + // Create a vector to store the data + std::vector v(size); + + // Load the data + file.read((char*)&v[0], size); + + return v; +} diff --git a/examples/readme.md b/examples/readme.md index 9a3e2965d0..635facb0d4 100644 --- a/examples/readme.md +++ b/examples/readme.md @@ -31,6 +31,7 @@ For a detailed explanations and API documentation see our [Documentation](../doc |[Pose and Image](./pose-and-image)|C++|Demonstrates how to use tracking camera asynchroniously to obtain 200Hz poses and 30Hz images | :star::star: |[![Motion Tracking - T260 and SLAM](https://img.shields.io/badge/-Tracking-0e2356.svg)](../doc/t265.md#examples-and-tools)| |[Apriltag Pose](./pose-apriltag)|C++|Demonstrates how to compute [Apriltag](https://github.com/AprilRobotics/apriltag/tree/3.1.1) pose from T265 fisheye image stream. | :star::star: |[![Motion Tracking - T260 and SLAM](https://img.shields.io/badge/-Tracking-0e2356.svg)](../doc/t265.md#examples-and-tools)| |[AR-Basic](./ar-basic)|C++|Shows how to use pose and fisheye frames to display a simple virtual object on the fisheye image | :star::star: |[![Motion Tracking - T260 and SLAM](https://img.shields.io/badge/-Tracking-0e2356.svg)](../doc/t265.md#examples-and-tools)| +|[AR-Advanced](./ar-advanced)|C++|Shows how to extend the [AR-Basic](./ar-basic) example with the relocalization API | :star::star::star: |[![Motion Tracking - T260 and SLAM](https://img.shields.io/badge/-Tracking-0e2356.svg)](../doc/t265.md#examples-and-tools)| |[DNN](../wrappers/opencv/dnn)| C++ & [OpenCV](https://github.com/IntelRealSense/librealsense/tree/master/wrappers/opencv#getting-started) | Intel RealSense camera used for real-time object-detection | :star::star: | [![Depth Sensing - Structured Light, Stereo and L500](https://img.shields.io/badge/-Depth-5bc3ff.svg)](./depth.md) | |[Tracking and Depth](./tracking-and-depth)| C++ | Shows how to use the tracking camera T265 together with a depth camera to display a 3D pointcloud with respect to a static reference frame | :star::star: | [![Depth Sensing - Structured Light, Stereo and L500](https://img.shields.io/badge/-Depth-5bc3ff.svg)](./depth.md) [![Motion Tracking - T260 and SLAM](https://img.shields.io/badge/-Tracking-0e2356.svg)](../doc/t265.md#examples-and-tools) |[Trajectory](./trajectory)| C++ | Shows how to calculate and render 3D trajectory based on pose data from a tracking camera | :star::star::star: | [![Motion Tracking - T260 and SLAM](https://img.shields.io/badge/-Tracking-0e2356.svg)](../doc/t265.md#examples-and-tools) From 2cbfb1969c298f60c28a1f3155222a107a772fe7 Mon Sep 17 00:00:00 2001 From: Brian Fulkerson Date: Thu, 5 Dec 2019 15:23:41 +0000 Subject: [PATCH 005/112] polling_device_watcher: fix race between construction and start Device watchers do not notify for devices that already exist when they are constructed (e.g. win_event_device_watcher), but polling_device_watcher was also not notifying if devices were added between construction and start. This aligns polling_device_watcher with win_event_device_watcher --- src/types.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/types.h b/src/types.h index 94e9f5ea87..b1137b0f04 100644 --- a/src/types.h +++ b/src/types.h @@ -1482,6 +1482,9 @@ namespace librealsense polling(cancellable_timer); }), _devices_data() { + _devices_data = { _backend->query_uvc_devices(), + _backend->query_usb_devices(), + _backend->query_hid_devices() }; } ~polling_device_watcher() @@ -1512,10 +1515,6 @@ namespace librealsense { stop(); _callback = std::move(callback); - _devices_data = { _backend->query_uvc_devices(), - _backend->query_usb_devices(), - _backend->query_hid_devices() }; - _active_object.start(); } From d0e10f7599c6f046b0943f4e41c877694a951ce2 Mon Sep 17 00:00:00 2001 From: "Hon Pong (Gary) Ho" Date: Fri, 6 Dec 2019 09:39:33 +0800 Subject: [PATCH 006/112] T2xx: rs-ar-advanced readme.md update clarify command line options, and word edits --- examples/ar-advanced/readme.md | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/examples/ar-advanced/readme.md b/examples/ar-advanced/readme.md index 11d4bc7b41..8a7382f5b0 100644 --- a/examples/ar-advanced/readme.md +++ b/examples/ar-advanced/readme.md @@ -3,24 +3,26 @@ > In order to run this example, a device supporting pose stream (T265) is required. ## Overview -This sample demonstrates how to extend the [`rs-ar-basic` Sample](../ar-basic) with map import/export API. -It also shows how to receive a relocalization event callback, and how to use the get and set static node API to add landmarks to the localization map. +This sample demonstrates how to extend the [`rs-ar-basic` Sample](../ar-basic) with `rs2::pose_sensor` API to: +* Import/export localization data file(s), +* Receive a relocalization event callback, +* Set/get static node to add/read landmark to/from the localization map. ## Command Line Inputs -Users should specify the path(s) to the localization map data file(s) by using command line arguments. For example, to set input map file `src_map.raw` at current directory: +Users should specify the path(s) to the localization map data file(s) by using command line arguments. For example, to load a map file, users can run the application `rs-ar-advanced` followed by a command line option `--load_map` and file path `src_map.raw` which is located at the same directory as `rs-ar-advanced`: ```cpp -rs-ar-advanced --load_map src_map.raw +>>rs-ar-advanced --load_map src_map.raw ``` -To set both input map file `src_map.raw` and output map file `dst_map.raw` at current directory: +To set both input map file `src_map.raw` and output map file `dst_map.raw` at same directory of the application: ```cpp -rs-ar-advanced --load_map src_map.raw --save_map dst_map.raw` +>>rs-ar-advanced --load_map src_map.raw --save_map dst_map.raw` ``` -Then, the application will import localization map data from `src_map.raw` at the beginning of tracking and will export the modified map including a virtual object as a static node to `dst_map.raw`. +Then, the application will import localization map data from `src_map.raw` file at the beginning of tracking and will export the modified map including a virtual object as a static node to `dst_map.raw` file. >Note: if neither input nor output path is given, this application will behave exactly the same as the `rs-ar-basic` example. @@ -55,7 +57,7 @@ tm_sensor.set_notifications_callback([&](const rs2::notification& n) { std::cout << "Relocalization Event Detected." << std::endl; ``` -Continue within the callback is that we load the virtual object saved as static node in the map file in previous run: +Continue within the callback is that we load the virtual object which had been saved as a static node in the map file in previous run: ```cpp // Get static node if available @@ -65,7 +67,7 @@ Continue within the callback is that we load the virtual object saved as static } ``` -The rest of the main body is similar to the [`rs-ar-basic` Sample](../ar-basic), except, at the end of the application we will save the modified virtual object as static node: +The rest of the main body is similar to the [`rs-ar-basic` Sample](../ar-basic), except, at the end of the application, we save the modified virtual object as a static node: ```cpp // Exit if user presses escape @@ -74,7 +76,7 @@ if (tm_sensor.set_static_node(virtual_object_guid, object_pose_in_world.translat } ``` -We also save the modified localization map to file path given by the command line inputs (if available): +Finally, we also save the modified localization map to file path given by the command line inputs (if available): ```cpp // Export map to a raw file From 67fb132a345843f958b5e178370e4ff3ff3e7770 Mon Sep 17 00:00:00 2001 From: Ariel Lowenstein Date: Thu, 5 Dec 2019 15:16:28 +0200 Subject: [PATCH 007/112] add new features to android - Terminal - FW Backup - FW logs --- src/android/CMakeLists.txt | 9 + src/android/fw-logger/fw-log-data.cpp | 57 ++++ src/android/fw-logger/fw-log-data.h | 91 ++++++ .../fw-logger/fw-logs-formating-options.cpp | 91 ++++++ .../fw-logger/fw-logs-formating-options.h | 45 +++ src/android/fw-logger/fw-logs-parser.cpp | 98 ++++++ src/android/fw-logger/fw-logs-parser.h | 29 ++ src/android/fw-logger/fw-logs-xml-helper.cpp | 297 ++++++++++++++++++ src/android/fw-logger/fw-logs-xml-helper.h | 47 +++ src/android/fw-logger/rs-fw-logger.cpp | 131 ++++++++ src/android/fw-logger/rs-fw-logger.h | 20 ++ src/android/fw-logger/string-formatter.cpp | 113 +++++++ src/android/fw-logger/string-formatter.h | 27 ++ src/android/jni/context.cpp | 2 + src/android/jni/debug_protocol.cpp | 202 ++++++++++++ src/android/jni/device.cpp | 7 +- src/android/jni/frame.cpp | 10 + src/android/jni/fw_logger.cpp | 28 ++ .../intel/realsense/capture/MainActivity.java | 8 +- .../realsense/multicam/MainActivity.java | 8 +- .../realsense/processing/MainActivity.java | 8 +- .../realsense/recording/MainActivity.java | 8 +- .../realsense/librealsense/DebugProtocol.java | 25 ++ .../intel/realsense/librealsense/Device.java | 1 + .../intel/realsense/librealsense/Frame.java | 7 +- .../realsense/librealsense/FwLogger.java | 15 + .../realsense/librealsense/Updatable.java | 9 + .../tools/camera/src/main/AndroidManifest.xml | 3 + .../realsense/camera/ControlsDialog.java | 40 +++ .../realsense/camera/DetachedActivity.java | 16 +- .../realsense/camera/FileBrowserActivity.java | 8 +- .../intel/realsense/camera/FileUtilities.java | 66 ++++ .../realsense/camera/PreviewActivity.java | 72 ++++- .../realsense/camera/RecordingActivity.java | 8 +- .../realsense/camera/SettingsActivity.java | 149 ++++++++- .../realsense/camera/StreamingStats.java | 163 ++++++++-- .../realsense/camera/TerminalActivity.java | 214 +++++++++++++ .../main/res/layout-land/activity_preview.xml | 38 ++- .../src/main/res/layout/activity_preview.xml | 16 + .../src/main/res/layout/activity_terminal.xml | 87 +++++ .../src/main/res/layout/controls_dialog.xml | 103 ++++++ .../camera/src/main/res/values/strings.xml | 4 + 42 files changed, 2306 insertions(+), 74 deletions(-) create mode 100644 src/android/fw-logger/fw-log-data.cpp create mode 100644 src/android/fw-logger/fw-log-data.h create mode 100644 src/android/fw-logger/fw-logs-formating-options.cpp create mode 100644 src/android/fw-logger/fw-logs-formating-options.h create mode 100644 src/android/fw-logger/fw-logs-parser.cpp create mode 100644 src/android/fw-logger/fw-logs-parser.h create mode 100644 src/android/fw-logger/fw-logs-xml-helper.cpp create mode 100644 src/android/fw-logger/fw-logs-xml-helper.h create mode 100644 src/android/fw-logger/rs-fw-logger.cpp create mode 100644 src/android/fw-logger/rs-fw-logger.h create mode 100644 src/android/fw-logger/string-formatter.cpp create mode 100644 src/android/fw-logger/string-formatter.h create mode 100644 src/android/jni/debug_protocol.cpp create mode 100644 src/android/jni/fw_logger.cpp create mode 100644 wrappers/android/librealsense/src/main/java/com/intel/realsense/librealsense/DebugProtocol.java create mode 100644 wrappers/android/librealsense/src/main/java/com/intel/realsense/librealsense/FwLogger.java create mode 100644 wrappers/android/tools/camera/src/main/java/com/intel/realsense/camera/ControlsDialog.java create mode 100644 wrappers/android/tools/camera/src/main/java/com/intel/realsense/camera/FileUtilities.java create mode 100644 wrappers/android/tools/camera/src/main/java/com/intel/realsense/camera/TerminalActivity.java create mode 100644 wrappers/android/tools/camera/src/main/res/layout/activity_terminal.xml create mode 100644 wrappers/android/tools/camera/src/main/res/layout/controls_dialog.xml diff --git a/src/android/CMakeLists.txt b/src/android/CMakeLists.txt index daa26eda80..de8d798faa 100644 --- a/src/android/CMakeLists.txt +++ b/src/android/CMakeLists.txt @@ -11,6 +11,7 @@ target_sources(${LRS_TARGET} "${CMAKE_CURRENT_LIST_DIR}/jni/error.h" "${CMAKE_CURRENT_LIST_DIR}/jni/error.cpp" "${CMAKE_CURRENT_LIST_DIR}/jni/context.cpp" + "${CMAKE_CURRENT_LIST_DIR}/jni/debug_protocol.cpp" "${CMAKE_CURRENT_LIST_DIR}/jni/device.cpp" "${CMAKE_CURRENT_LIST_DIR}/jni/sensor.cpp" "${CMAKE_CURRENT_LIST_DIR}/jni/device_list.cpp" @@ -19,10 +20,18 @@ target_sources(${LRS_TARGET} "${CMAKE_CURRENT_LIST_DIR}/jni/stream_profile.cpp" "${CMAKE_CURRENT_LIST_DIR}/jni/frame.cpp" "${CMAKE_CURRENT_LIST_DIR}/jni/frameset.cpp" + "${CMAKE_CURRENT_LIST_DIR}/jni/fw_logger.cpp" "${CMAKE_CURRENT_LIST_DIR}/jni/frame_queue.cpp" "${CMAKE_CURRENT_LIST_DIR}/jni/processing.cpp" "${CMAKE_CURRENT_LIST_DIR}/jni/options.cpp" "${CMAKE_CURRENT_LIST_DIR}/jni/advanced_mode.cpp" + + "${CMAKE_CURRENT_LIST_DIR}/fw-logger/fw-log-data.cpp" + "${CMAKE_CURRENT_LIST_DIR}/fw-logger/fw-logs-formating-options.cpp" + "${CMAKE_CURRENT_LIST_DIR}/fw-logger/fw-logs-parser.cpp" + "${CMAKE_CURRENT_LIST_DIR}/fw-logger/fw-logs-xml-helper.cpp" + "${CMAKE_CURRENT_LIST_DIR}/fw-logger/rs-fw-logger.cpp" + "${CMAKE_CURRENT_LIST_DIR}/fw-logger/string-formatter.cpp" ) message(STATUS "Prepare RealSense SDK for Android OS (${ANDROID_NDK_ABI_NAME})") diff --git a/src/android/fw-logger/fw-log-data.cpp b/src/android/fw-logger/fw-log-data.cpp new file mode 100644 index 0000000000..8183b78860 --- /dev/null +++ b/src/android/fw-logger/fw-log-data.cpp @@ -0,0 +1,57 @@ +// License: Apache 2.0. See LICENSE file in root directory. +// Copyright(c) 2019 Intel Corporation. All Rights Reserved. + +#include "fw-log-data.h" + +#include +#include +#include +#include + +using namespace std; + +# define SET_WIDTH_AND_FILL(num, element) \ +setfill(' ') << setw(num) << left << element \ + +namespace fw_logger +{ + fw_log_data::fw_log_data(void) + { + magic_number = 0; + severity = 0; + file_id = 0; + group_id = 0; + event_id = 0; + line = 0; + sequence = 0; + p1 = 0; + p2 = 0; + p3 = 0; + timestamp = 0; + delta = 0; + message = ""; + file_name = ""; + } + + fw_log_data::~fw_log_data(void) + { + } + + string fw_log_data::to_string() + { + stringstream fmt; + + fmt << SET_WIDTH_AND_FILL(6, sequence) + << SET_WIDTH_AND_FILL(30, file_name) + << SET_WIDTH_AND_FILL(6, group_id) + << SET_WIDTH_AND_FILL(15, thread_name) + << SET_WIDTH_AND_FILL(6, severity) + << SET_WIDTH_AND_FILL(6, line) + << SET_WIDTH_AND_FILL(15, timestamp) + << SET_WIDTH_AND_FILL(15, delta) + << SET_WIDTH_AND_FILL(30, message); + + auto str = fmt.str(); + return str; + } +} diff --git a/src/android/fw-logger/fw-log-data.h b/src/android/fw-logger/fw-log-data.h new file mode 100644 index 0000000000..99666cf132 --- /dev/null +++ b/src/android/fw-logger/fw-log-data.h @@ -0,0 +1,91 @@ +/* License: Apache 2.0. See LICENSE file in root directory. */ +/* Copyright(c) 2019 Intel Corporation. All Rights Reserved. */ + +#pragma once + +#include +#include +#include + +namespace fw_logger +{ + struct fw_logs_binary_data + { + std::vector logs_buffer; + }; + + typedef union + { + uint32_t value; + struct + { + uint32_t magic_number : 8; + uint32_t severity : 5; + uint32_t thread_id : 3; + uint32_t file_id : 11; + uint32_t group_id : 5; + } bits; + } fw_log_header_dword1; + + typedef union + { + uint32_t value; + struct + { + uint32_t event_id : 16; + uint32_t line_id : 12; + uint32_t seq_id : 4; + } bits; + } fw_log_header_dword2; + + struct fw_log_header_dword3 + { + uint16_t p1; + uint16_t p2; + }; + + struct fw_log_header_dword4 + { + uint32_t p3; + }; + + struct fw_log_header_dword5 + { + uint32_t timestamp; + }; + + struct fw_log_binary + { + fw_log_header_dword1 dword1; + fw_log_header_dword2 dword2; + fw_log_header_dword3 dword3; + fw_log_header_dword4 dword4; + fw_log_header_dword5 dword5; + }; + + class fw_log_data + { + public: + fw_log_data(void); + ~fw_log_data(void); + + uint32_t magic_number; + uint32_t severity; + uint32_t file_id; + uint32_t group_id; + uint32_t event_id; + uint32_t line; + uint32_t sequence; + uint32_t p1; + uint32_t p2; + uint32_t p3; + uint64_t timestamp; + double delta; + + std::string message; + std::string file_name; + std::string thread_name; + + std::string to_string(); + }; +} diff --git a/src/android/fw-logger/fw-logs-formating-options.cpp b/src/android/fw-logger/fw-logs-formating-options.cpp new file mode 100644 index 0000000000..86284927cc --- /dev/null +++ b/src/android/fw-logger/fw-logs-formating-options.cpp @@ -0,0 +1,91 @@ +// License: Apache 2.0. See LICENSE file in root directory. +// Copyright(c) 2019 Intel Corporation. All Rights Reserved. + +#include "fw-logs-formating-options.h" + +#include + +#include "fw-logs-xml-helper.h" + +using namespace std; + +namespace fw_logger +{ + fw_log_event::fw_log_event() + : num_of_params(0), + line("") + {} + + fw_log_event::fw_log_event(int input_num_of_params, const string& input_line) + : num_of_params(input_num_of_params), + line(input_line) + {} + + fw_logs_formating_options::fw_logs_formating_options(const string& xml_full_file_path) + : _xml_full_file_path(xml_full_file_path) + {} + + fw_logs_formating_options::~fw_logs_formating_options(void) + {} + + bool fw_logs_formating_options::get_event_data(int id, fw_log_event* log_event_data) const + { + auto event_it = _fw_logs_event_list.find(id); + if (event_it != _fw_logs_event_list.end()) + { + *log_event_data = event_it->second; + return true; + } + else + { + stringstream ss; + ss << "*** Unrecognized Log Id: "; + ss << id; + ss << "! P1 = 0x{0:x}, P2 = 0x{1:x}, P3 = 0x{2:x}"; + log_event_data->line = ss.str(); + log_event_data->num_of_params = 3; + return false; + } + } + + bool fw_logs_formating_options::get_file_name(int id, string* file_name) const + { + auto file_it = _fw_logs_file_names_list.find(id); + if (file_it != _fw_logs_file_names_list.end()) + { + *file_name = file_it->second; + return true; + } + else + { + *file_name = "Unknown"; + return false; + } + } + + bool fw_logs_formating_options::get_thread_name(uint32_t thread_id, string* thread_name) const + { + auto file_it = _fw_logs_thread_names_list.find(thread_id); + if (file_it != _fw_logs_thread_names_list.end()) + { + *thread_name = file_it->second; + return true; + } + else + { + *thread_name = "Unknown"; + return false; + } + } + + std::unordered_map> fw_logs_formating_options::get_enums() const + { + return _fw_logs_enum_names_list; + } + + bool fw_logs_formating_options::initialize_from_xml() + { + fw_logs_xml_helper fw_logs_xml(_xml_full_file_path); + return fw_logs_xml.build_log_meta_data(this); + } +} diff --git a/src/android/fw-logger/fw-logs-formating-options.h b/src/android/fw-logger/fw-logs-formating-options.h new file mode 100644 index 0000000000..0838fc9631 --- /dev/null +++ b/src/android/fw-logger/fw-logs-formating-options.h @@ -0,0 +1,45 @@ +/* License: Apache 2.0. See LICENSE file in root directory. */ +/* Copyright(c) 2019 Intel Corporation. All Rights Reserved. */ + +#pragma once + +#include +#include +#include +#include + +namespace fw_logger +{ + struct fw_log_event + { + int num_of_params; + std::string line; + + fw_log_event(); + fw_log_event(int input_num_of_params, const std::string& input_line); + }; + + class fw_logs_xml_helper; + + class fw_logs_formating_options + { + public: + fw_logs_formating_options(const std::string& xml_full_file_path); + ~fw_logs_formating_options(void); + + bool get_event_data(int id, fw_log_event* log_event_data) const; + bool get_file_name(int id, std::string* file_name) const; + bool get_thread_name(uint32_t thread_id, std::string* thread_name) const; + std::unordered_map> get_enums() const; + bool initialize_from_xml(); + + private: + friend fw_logs_xml_helper; + std::unordered_map _fw_logs_event_list; + std::unordered_map _fw_logs_file_names_list; + std::unordered_map _fw_logs_thread_names_list; + std::unordered_map> _fw_logs_enum_names_list; + + std::string _xml_full_file_path; + }; +} diff --git a/src/android/fw-logger/fw-logs-parser.cpp b/src/android/fw-logger/fw-logs-parser.cpp new file mode 100644 index 0000000000..7299af6847 --- /dev/null +++ b/src/android/fw-logger/fw-logs-parser.cpp @@ -0,0 +1,98 @@ +// License: Apache 2.0. See LICENSE file in root directory. +// Copyright(c) 2019 Intel Corporation. All Rights Reserved. + +#include "fw-logs-parser.h" + +#include +#include +#include "string-formatter.h" +#include "stdint.h" + +using namespace std; + +namespace fw_logger +{ + fw_logs_parser::fw_logs_parser(string xml_full_file_path) + : _fw_logs_formating_options(xml_full_file_path), + _last_timestamp(0), + _timestamp_factor(0.00001) + { + _fw_logs_formating_options.initialize_from_xml(); + } + + fw_logs_parser::~fw_logs_parser(void) + {} + + vector fw_logs_parser::get_fw_log_lines(const fw_logs_binary_data& fw_logs_data_binary) + { + vector string_vector; + int num_of_lines = int(fw_logs_data_binary.logs_buffer.size()) / sizeof(fw_log_binary); + auto temp_pointer = reinterpret_cast(fw_logs_data_binary.logs_buffer.data()); + + for (int i = 0; i < num_of_lines; i++) + { + string line; + auto log = const_cast(reinterpret_cast(temp_pointer)); + line = generate_log_line(log); + string_vector.push_back(line); + temp_pointer++; + } + return string_vector; + } + + string fw_logs_parser::generate_log_line(char* fw_logs) + { + fw_log_data log_data; + fill_log_data(fw_logs, &log_data); + + return log_data.to_string(); + } + + void fw_logs_parser::fill_log_data(const char* fw_logs, fw_log_data* log_data) + { + string_formatter reg_exp(_fw_logs_formating_options.get_enums()); + fw_log_event log_event_data; + + auto* log_binary = reinterpret_cast(fw_logs); + + //parse first DWORD + log_data->magic_number = static_cast(log_binary->dword1.bits.magic_number); + log_data->severity = static_cast(log_binary->dword1.bits.severity); + + log_data->file_id = static_cast(log_binary->dword1.bits.file_id); + log_data->group_id = static_cast(log_binary->dword1.bits.group_id); + + //parse second DWORD + log_data->event_id = static_cast(log_binary->dword2.bits.event_id); + log_data->line = static_cast(log_binary->dword2.bits.line_id); + log_data->sequence = static_cast(log_binary->dword2.bits.seq_id); + + //parse third DWORD + log_data->p1 = static_cast(log_binary->dword3.p1); + log_data->p2 = static_cast(log_binary->dword3.p2); + + //parse forth DWORD + log_data->p3 = static_cast(log_binary->dword4.p3); + + //parse fifth DWORD + log_data->timestamp = log_binary->dword5.timestamp; + + if (_last_timestamp == 0) + { + log_data->delta = 0; + } + else + { + log_data->delta = (log_data->timestamp - _last_timestamp)*_timestamp_factor; + } + + _last_timestamp = log_data->timestamp; + _fw_logs_formating_options.get_event_data(log_data->event_id, &log_event_data); + uint32_t params[3] = { log_data->p1, log_data->p2, log_data->p3 }; + reg_exp.generate_message(log_event_data.line, log_event_data.num_of_params, params, &log_data->message); + + _fw_logs_formating_options.get_file_name(log_data->file_id, &log_data->file_name); + _fw_logs_formating_options.get_thread_name(static_cast(log_binary->dword1.bits.thread_id), + &log_data->thread_name); + } +} diff --git a/src/android/fw-logger/fw-logs-parser.h b/src/android/fw-logger/fw-logs-parser.h new file mode 100644 index 0000000000..48254e4ac3 --- /dev/null +++ b/src/android/fw-logger/fw-logs-parser.h @@ -0,0 +1,29 @@ +/* License: Apache 2.0. See LICENSE file in root directory. */ +/* Copyright(c) 2019 Intel Corporation. All Rights Reserved. */ + +#pragma once + +#include +#include + +#include "fw-logs-formating-options.h" +#include "fw-log-data.h" + +namespace fw_logger +{ + class fw_logs_parser + { + public: + explicit fw_logs_parser(std::string xml_full_file_path); + ~fw_logs_parser(void); + std::vector get_fw_log_lines(const fw_logs_binary_data& fw_logs_data_binary); + + private: + std::string generate_log_line(char* fw_logs); + void fill_log_data(const char* fw_logs, fw_log_data* log_data); + uint64_t _last_timestamp; + + fw_logs_formating_options _fw_logs_formating_options; + const double _timestamp_factor; + }; +} diff --git a/src/android/fw-logger/fw-logs-xml-helper.cpp b/src/android/fw-logger/fw-logs-xml-helper.cpp new file mode 100644 index 0000000000..147d159525 --- /dev/null +++ b/src/android/fw-logger/fw-logs-xml-helper.cpp @@ -0,0 +1,297 @@ +// License: Apache 2.0. See LICENSE file in root directory. +// Copyright(c) 2019 Intel Corporation. All Rights Reserved. + +#include "fw-logs-xml-helper.h" + +#include +#include +#include +#include + +using namespace std; + +namespace fw_logger +{ + fw_logs_xml_helper::fw_logs_xml_helper(string xml_full_file_path) + : _init_done(false), + _xml_full_file_path(xml_full_file_path) + {} + + fw_logs_xml_helper::~fw_logs_xml_helper(void) + {} + + bool fw_logs_xml_helper::get_root_node(xml_node<> **node) + { + if (_init_done) + { + *node = _xml_doc.first_node(); + return true; + } + + return false; + } + + bool fw_logs_xml_helper::try_load_external_xml() + { + try + { + if (_xml_full_file_path.empty()) + return false; + + rapidxml::file<> xml_file(_xml_full_file_path.c_str()); + + _document_buffer.resize(xml_file.size() + 2); + memcpy(_document_buffer.data(), xml_file.data(), xml_file.size()); + _document_buffer[xml_file.size()] = '\0'; + _document_buffer[xml_file.size() + 1] = '\0'; + _xml_doc.parse<0>(_document_buffer.data()); + + return true; + } + catch (...) + { + _document_buffer.clear(); + throw; + } + + return false; + } + + bool fw_logs_xml_helper::init() + { + _init_done = try_load_external_xml(); + return _init_done; + } + + bool fw_logs_xml_helper::build_log_meta_data(fw_logs_formating_options* log_meta_data) + { + xml_node<> *xml_root_node_list; + + if (!init()) + return false; + + if (!get_root_node(&xml_root_node_list)) + { + return false; + } + + string root_name(xml_root_node_list->name(), xml_root_node_list->name() + xml_root_node_list->name_size()); + + // check if Format is the first root name. + if (root_name.compare("Format") != 0) + return false; + + xml_node<>* events_node = xml_root_node_list->first_node(); + + + if (!build_meta_data_structure(events_node, log_meta_data)) + return false; + + return true; + } + + bool fw_logs_xml_helper::build_meta_data_structure(xml_node<> *xml_node_list_of_events, fw_logs_formating_options* logs_formating_options) + { + node_type res = none; + int id; + int num_of_params; + string line; + + // loop through all elements in the Format. + for (xml_node<>* node = xml_node_list_of_events; node; node = node->next_sibling()) + { + line.clear(); + res = get_next_node(node, &id, &num_of_params, &line); + if (res == event) + { + fw_log_event log_event(num_of_params, line); + logs_formating_options->_fw_logs_event_list.insert(pair(id, log_event)); + } + else if (res == file) + { + logs_formating_options->_fw_logs_file_names_list.insert(pair(id, line)); + } + else if (res == thread) + { + logs_formating_options->_fw_logs_thread_names_list.insert(pair(id, line)); + } + else if (res == enums) + { + for (xml_node<>* enum_node = node->first_node(); enum_node; enum_node = enum_node->next_sibling()) + { + for (xml_attribute<>* attribute = enum_node->first_attribute(); attribute; attribute = attribute->next_attribute()) + { + string attr(attribute->name(), attribute->name() + attribute->name_size()); + if (attr.compare("Name") == 0) + { + string name_attr_str(attribute->value(), attribute->value() + attribute->value_size()); + vector values; + + for (xml_node<>* enum_value_node = enum_node->first_node(); enum_value_node; enum_value_node = enum_value_node->next_sibling()) + { + for (xml_attribute<>* attribute = enum_value_node->first_attribute(); attribute; attribute = attribute->next_attribute()) + { + string attr(attribute->name(), attribute->name() + attribute->name_size()); + if (attr.compare("Value") == 0) + { + string value_str(attribute->value(), attribute->value() + attribute->value_size()); + values.push_back(value_str); + } + } + } + logs_formating_options->_fw_logs_enum_names_list.insert(pair>(name_attr_str, values)); + } + } + } + } + else + return false; + } + + return true; + } + + fw_logs_xml_helper::node_type fw_logs_xml_helper::get_next_node(xml_node<> *node, int* id, int* num_of_params, string* line) + { + + string tag(node->name(), node->name() + node->name_size()); + + if (tag.compare("Event") == 0) + { + if (get_event_node(node, id, num_of_params, line)) + return event; + } + else if (tag.compare("File") == 0) + { + if (get_file_node(node, id, line)) + return file; + } + else if (tag.compare("Thread") == 0) + { + if (get_thread_node(node, id, line)) + return thread; + } + else if (tag.compare("Enums") == 0) + { + return enums; + } + return none; + } + + bool fw_logs_xml_helper::get_enum_name_node(xml_node<>* node_file, int* thread_id, string* enum_name) + { + for (xml_attribute<>* attribute = node_file->first_attribute(); attribute; attribute = attribute->next_attribute()) + { + string attr(attribute->name(), attribute->name() + attribute->name_size()); + + if (attr.compare("Name") == 0) + { + string name_attr_str(attribute->value(), attribute->value() + attribute->value_size()); + *enum_name = name_attr_str; + continue; + } + else + return false; + } + + return true; + } + + bool fw_logs_xml_helper::get_enum_value_node(xml_node<>* node_file, int* thread_id, string* enum_name) + { + for (xml_attribute<>* attribute = node_file->first_attribute(); attribute; attribute = attribute->next_attribute()) + { + string attr(attribute->name(), attribute->name() + attribute->name_size()); + + if (attr.compare("Value") == 0) + { + string name_attr_str(attribute->value(), attribute->value() + attribute->value_size()); + *enum_name = name_attr_str; + continue; + } + else + return false; + } + + return true; + } + + bool fw_logs_xml_helper::get_thread_node(xml_node<>* node_file, int* thread_id, string* thread_name) + { + for (xml_attribute<>* attribute = node_file->first_attribute(); attribute; attribute = attribute->next_attribute()) + { + string attr(attribute->name(), attribute->name() + attribute->name_size()); + + if (attr.compare("id") == 0) + { + string id_attr_str(attribute->value(), attribute->value() + attribute->value_size()); + *thread_id = stoi(id_attr_str); + continue; + } + else if (attr.compare("Name") == 0) + { + string name_attr_str(attribute->value(), attribute->value() + attribute->value_size()); + *thread_name = name_attr_str; + continue; + } + else + return false; + } + + return true; + } + + bool fw_logs_xml_helper::get_file_node(xml_node<>* node_file, int* file_id, string* file_name) + { + for (xml_attribute<>* attribute = node_file->first_attribute(); attribute; attribute = attribute->next_attribute()) + { + string attr(attribute->name(), attribute->name() + attribute->name_size()); + + if (attr.compare("id") == 0) + { + string id_attr_str(attribute->value(), attribute->value() + attribute->value_size()); + *file_id = stoi(id_attr_str); + continue; + } + else if (attr.compare("Name") == 0) + { + string name_attr_str(attribute->value(), attribute->value() + attribute->value_size()); + *file_name = name_attr_str; + continue; + } + else + return false; + } + return true; + } + + bool fw_logs_xml_helper::get_event_node(xml_node<>* node_event, int* event_id, int* num_of_params, string* line) + { + for (xml_attribute<>* attribute = node_event->first_attribute(); attribute; attribute = attribute->next_attribute()) + { + string attr(attribute->name(), attribute->name() + attribute->name_size()); + + if (attr.compare("id") == 0) + { + string id_attr_str(attribute->value(), attribute->value() + attribute->value_size()); + *event_id = stoi(id_attr_str); + continue; + } + else if (attr.compare("numberOfArguments") == 0) + { + string num_of_args_attr_str(attribute->value(), attribute->value() + attribute->value_size()); + *num_of_params = stoi(num_of_args_attr_str); + continue; + } + else if (attr.compare("format") == 0) + { + string format_attr_str(attribute->value(), attribute->value() + attribute->value_size()); + *line = format_attr_str; + continue; + } + else + return false; + } + return true; + } +} diff --git a/src/android/fw-logger/fw-logs-xml-helper.h b/src/android/fw-logger/fw-logs-xml-helper.h new file mode 100644 index 0000000000..9ecddeed39 --- /dev/null +++ b/src/android/fw-logger/fw-logs-xml-helper.h @@ -0,0 +1,47 @@ +/* License: Apache 2.0. See LICENSE file in root directory. */ +/* Copyright(c) 2019 Intel Corporation. All Rights Reserved. */ + +#pragma once + +#include "../../../third-party/rapidxml/rapidxml_utils.hpp" +#include "fw-logs-formating-options.h" + +using namespace rapidxml; + +namespace fw_logger +{ + class fw_logs_xml_helper + { + public: + enum node_type + { + event, + file, + thread, + enums, + none + }; + + fw_logs_xml_helper(std::string xml_full_file_path); + ~fw_logs_xml_helper(void); + + bool build_log_meta_data(fw_logs_formating_options* logs_formating_options); + + private: + bool init(); + bool build_meta_data_structure(xml_node<> *xml_node_list_of_events, fw_logs_formating_options* logs_formating_options); + node_type get_next_node(xml_node<>* xml_node_list_of_events, int* id, int* num_of_params, std::string* line); + bool get_thread_node(xml_node<>* node_file, int* thread_id, std::string* thread_name); + bool get_event_node(xml_node<>* node_event, int* event_id, int* num_of_params, std::string* line); + bool get_enum_name_node(xml_node<>* node_file, int* thread_id, std::string* thread_name); + bool get_enum_value_node(xml_node<>* node_file, int* thread_id, std::string* enum_name); + bool get_file_node(xml_node<>* node_file, int* file_id, std::string* file_name); + bool get_root_node(xml_node<> **node); + bool try_load_external_xml(); + + bool _init_done; + std::string _xml_full_file_path; + xml_document<> _xml_doc; + std::vector _document_buffer; + }; +} diff --git a/src/android/fw-logger/rs-fw-logger.cpp b/src/android/fw-logger/rs-fw-logger.cpp new file mode 100644 index 0000000000..9b80cbc92a --- /dev/null +++ b/src/android/fw-logger/rs-fw-logger.cpp @@ -0,0 +1,131 @@ +// License: Apache 2.0. See LICENSE file in root directory. +// Copyright(c) 2015 Intel Corporation. All Rights Reserved. + +#include "rs-fw-logger.h" + +#include +#include +#include +#include +#include + +#include "fw-logs-parser.h" +#include "../../../include/librealsense2/hpp/rs_context.hpp" + +#define TAG "rs_fw_log" + +void log(std::string message) +{ + __android_log_print(ANDROID_LOG_INFO, TAG, "%s", message.c_str()); +} + +std::string hexify(unsigned char n) +{ + std::string res; + + do + { + res += "0123456789ABCDEF"[n % 16]; + n >>= 4; + } while (n); + + std::reverse(res.begin(), res.end()); + + if (res.size() == 1) + { + res.insert(0, "0"); + } + + return res; +} + +void android_fw_logger::read_log_loop() +{ + _active = true; + + std::unique_ptr fw_log_parser; + auto use_xml_file = false; + if (!_xml_path.empty()) + { + std::ifstream f(_xml_path); + if (f.good()) + { + fw_log_parser = std::unique_ptr(new fw_logger::fw_logs_parser(_xml_path)); + use_xml_file = true; + } + } + + while(_active) + { try + { + rs2::context ctx; + auto devs = ctx.query_devices(); + if(devs.size() == 0){ + _active = false; + break; + } + auto dev = ctx.query_devices()[0]; + + std::vector input; + auto str_op_code = dev.get_info(RS2_CAMERA_INFO_DEBUG_OP_CODE); + auto op_code = static_cast(std::stoi(str_op_code)); + input = {0x14, 0x00, 0xab, 0xcd, op_code, 0x00, 0x00, 0x00, + 0xf4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + std::stringstream dev_info; + dev_info << "Device Name: " << dev.get_info(RS2_CAMERA_INFO_NAME) << "\nDevice Location: " << dev.get_info(RS2_CAMERA_INFO_PHYSICAL_PORT) << "\n\n"; + log(dev_info.str()); + + while (_active) + { + std::this_thread::sleep_for(std::chrono::milliseconds(_sample_rate)); + + auto raw_data = dev.as().send_and_receive_raw_data(input); + std::vector fw_log_lines = {""}; + if (raw_data.size() <= 4) + continue; + + if (use_xml_file) + { + fw_logger::fw_logs_binary_data fw_logs_binary_data = {raw_data}; + fw_logs_binary_data.logs_buffer.erase(fw_logs_binary_data.logs_buffer.begin(),fw_logs_binary_data.logs_buffer.begin()+4); + fw_log_lines = fw_log_parser->get_fw_log_lines(fw_logs_binary_data); + } + else + { + std::stringstream sstr; + sstr << "FW_Log_Data:"; + for (size_t i = 0; i < raw_data.size(); ++i) + sstr << hexify(raw_data[i]) << " "; + + fw_log_lines.push_back(sstr.str()); + } + + for (auto& line : fw_log_lines) + log(line); + + } + } + catch (const rs2::error & e) + { + std::stringstream cerr; + cerr << "RealSense error calling " << e.get_failed_function() << "(" << e.get_failed_args() << "):\n " << e.what(); + + log(cerr.str()); + } + } +} + +android_fw_logger::android_fw_logger(std::string xml_path, int sample_rate) : _xml_path(xml_path), _sample_rate(sample_rate) +{ + log("StartReadingFwLogs"); + _thread = std::thread(&android_fw_logger::read_log_loop, this); +} + +android_fw_logger::~android_fw_logger() +{ + log("StopReadingFwLogs"); + _active = false; + _thread.join(); +} diff --git a/src/android/fw-logger/rs-fw-logger.h b/src/android/fw-logger/rs-fw-logger.h new file mode 100644 index 0000000000..f322838a72 --- /dev/null +++ b/src/android/fw-logger/rs-fw-logger.h @@ -0,0 +1,20 @@ +// License: Apache 2.0. See LICENSE file in root directory. +// Copyright(c) 2015 Intel Corporation. All Rights Reserved. + +#include +#include + +class android_fw_logger +{ +public: + android_fw_logger(std::string xml_path = "", int sample_rate = 100); + ~android_fw_logger(); + +private: + bool _active = false; + std::thread _thread; + std::string _xml_path; + + void read_log_loop(); + int _sample_rate; +}; diff --git a/src/android/fw-logger/string-formatter.cpp b/src/android/fw-logger/string-formatter.cpp new file mode 100644 index 0000000000..d768f9d7c3 --- /dev/null +++ b/src/android/fw-logger/string-formatter.cpp @@ -0,0 +1,113 @@ +// License: Apache 2.0. See LICENSE file in root directory. +// Copyright(c) 2019 Intel Corporation. All Rights Reserved. + +#include "string-formatter.h" + +#include +#include +#include + +using namespace std; + +namespace fw_logger +{ + string_formatter::string_formatter(std::unordered_map> enums) + :_enums(enums) + { + } + + string_formatter::~string_formatter(void) + { + } + + bool string_formatter::generate_message(const string& source, int num_of_params, const uint32_t* params, string* dest) + { + map exp_replace_map; + map enum_replace_map; + + if (params == nullptr && num_of_params > 0) return false; + + for (int i = 0; i < num_of_params; i++) + { + string regular_exp[3]; + string replacement[3]; + stringstream st_regular_exp[3]; + stringstream st_replacement[3]; + + st_regular_exp[0] << "\\{\\b(" << i << ")\\}"; + regular_exp[0] = st_regular_exp[0].str(); + + st_replacement[0] << params[i]; + replacement[0] = st_replacement[0].str(); + + exp_replace_map[regular_exp[0]] = replacement[0]; + + + st_regular_exp[1] << "\\{\\b(" << i << "):x\\}"; + regular_exp[1] = st_regular_exp[1].str(); + + st_replacement[1] << hex << setw(2) << setfill('0') << params[i]; + replacement[1] = st_replacement[1].str(); + + exp_replace_map[regular_exp[1]] = replacement[1]; + + st_regular_exp[2] << "\\{\\b(" << i << "),[a-zA-Z]+\\}"; + regular_exp[2] = st_regular_exp[2].str(); + + enum_replace_map[regular_exp[2]] = params[i]; + } + + return replace_params(source, exp_replace_map, enum_replace_map, dest); + } + + bool string_formatter::replace_params(const string& source, const map& exp_replace_map, const map& enum_replace_map, string* dest) + { + string source_temp(source); + + for (auto exp_replace_it = exp_replace_map.begin(); exp_replace_it != exp_replace_map.end(); exp_replace_it++) + { + string destTemp; + regex e(exp_replace_it->first); + auto res = regex_replace(back_inserter(destTemp), source_temp.begin(), source_temp.end(), e, exp_replace_it->second); + source_temp = destTemp; + } + + for (auto exp_replace_it = enum_replace_map.begin(); exp_replace_it != enum_replace_map.end(); exp_replace_it++) + { + string destTemp; + regex e(exp_replace_it->first); + std::smatch m; + std::regex_search(source_temp, m, std::regex(e)); + + string enum_name; + + string st_regular_exp = "[a-zA-Z]+"; + regex e1(st_regular_exp); + + for(auto exp = 0; exp0 && _enums.find(enum_name) != _enums.end()) + { + auto vec = _enums[enum_name]; + regex e3 = e; + auto res1 = regex_replace(back_inserter(destTemp), source_temp.begin(), source_temp.end(), e3, vec[exp_replace_it->second]); + source_temp = destTemp; + } + } + } + } + + *dest = source_temp; + return true; + } +} diff --git a/src/android/fw-logger/string-formatter.h b/src/android/fw-logger/string-formatter.h new file mode 100644 index 0000000000..6c27cd3a18 --- /dev/null +++ b/src/android/fw-logger/string-formatter.h @@ -0,0 +1,27 @@ +/* License: Apache 2.0. See LICENSE file in root directory. */ +/* Copyright(c) 2019 Intel Corporation. All Rights Reserved. */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace fw_logger +{ + class string_formatter + { + public: + string_formatter(std::unordered_map> enums); + ~string_formatter(void); + + bool generate_message(const std::string& source, int num_of_params, const uint32_t* params, std::string* dest); + + private: + bool replace_params(const std::string& source, const std::map& exp_replace_map, const std::map& enum_replace_map, std::string* dest); + + std::unordered_map> _enums; + }; +} diff --git a/src/android/jni/context.cpp b/src/android/jni/context.cpp index bca36f0a21..fe533d975f 100644 --- a/src/android/jni/context.cpp +++ b/src/android/jni/context.cpp @@ -5,6 +5,8 @@ #include "error.h" #include "../../../include/librealsense2/rs.h" +#include "../../../include/librealsense2/hpp/rs_context.hpp" + extern "C" JNIEXPORT jlong JNICALL Java_com_intel_realsense_librealsense_RsContext_nCreate(JNIEnv *env, jclass type) { rs2_error* e = NULL; diff --git a/src/android/jni/debug_protocol.cpp b/src/android/jni/debug_protocol.cpp new file mode 100644 index 0000000000..9437e0ea37 --- /dev/null +++ b/src/android/jni/debug_protocol.cpp @@ -0,0 +1,202 @@ +// License: Apache 2.0. See LICENSE file in root directory. +// Copyright(c) 2019 Intel Corporation. All Rights Reserved. + +#include +#include +#include +#include "error.h" +#include +#include +#include + +#include "../../../include/librealsense2/rs.h" +#include "../../../include/librealsense2/hpp/rs_device.hpp" +#include "../../api.h" +#include "../../../common/parser.hpp" + +using std::string; +using std::hex; + +std::vector send_and_receive_raw_data(JNIEnv *env, rs2_device * dev, const std::vector& input) +{ + std::vector results; + + rs2_error *e = NULL; + std::shared_ptr list( + rs2_send_and_receive_raw_data(dev, (void*)input.data(), (uint32_t)input.size(), &e), + rs2_delete_raw_data); + handle_error(env, e); + + auto size = rs2_get_raw_data_size(list.get(), &e); + handle_error(env, e); + + auto start = rs2_get_raw_data(list.get(), &e); + + results.insert(results.begin(), start, start + size); + + return results; +} + +std::string hex_mode(JNIEnv *env, rs2_device * dev, const std::string& line) +{ + std::vector raw_data; + std::stringstream ss(line); + std::string word; + while (ss >> word) + { + std::stringstream converter; + int temp; + converter << std::hex << word; + converter >> temp; + raw_data.push_back(temp); + } + if (raw_data.empty()) + throw std::runtime_error("Wrong input!"); + + auto result = send_and_receive_raw_data(env, dev, raw_data); + + std::stringstream rv; + for (auto& elem : result) + rv << std::setfill('0') << std::setw(2) << std::hex << static_cast(elem) << " "; + + return rv.str(); +} + +std::vector build_raw_command_data(const command& command, const std::vector& params) +{ + if (params.size() > command.parameters.size() && !command.is_cmd_write_data) + throw std::runtime_error("Input string was not in a correct format!"); + + std::vector vec_parameters; + for (auto param_index = 0; param_index < params.size(); ++param_index) + { + auto is_there_write_data = param_index >= int(command.parameters.size()); + auto name = (is_there_write_data) ? "" : command.parameters[param_index].name; + auto is_reverse_bytes = (is_there_write_data) ? false : command.parameters[param_index].is_reverse_bytes; + auto is_decimal = (is_there_write_data) ? false : command.parameters[param_index].is_decimal; + auto format_length = (is_there_write_data) ? -1 : command.parameters[param_index].format_length; + vec_parameters.push_back(parameter(name, params[param_index], is_decimal, is_reverse_bytes, format_length)); + } + + std::vector raw_data; + encode_raw_data_command(command, vec_parameters, raw_data); + return raw_data; +} + +std::string xml_mode(const std::string& line, const commands_xml& cmd_xml, std::map& format_type_to_lambda, JNIEnv *env, jlong handle) +{ + try { + std::vector tokens; + std::stringstream ss(line); + std::string word; + while (ss >> word) { + std::stringstream converter; + converter << hex << word; + tokens.push_back(word); + } + + if (tokens.empty()) + throw std::runtime_error("Wrong input!"); + + auto command_str = tokens.front(); + auto it = cmd_xml.commands.find(command_str); + if (it == cmd_xml.commands.end()) + throw std::runtime_error("Command not found!"); + + auto command = it->second; + std::vector params; + for (auto i = 1; i < tokens.size(); ++i) + params.push_back(tokens[i]); + + auto raw_data = build_raw_command_data(command, params); + + auto result = send_and_receive_raw_data(env, reinterpret_cast(handle), + raw_data); + + unsigned returned_opcode = *result.data(); + // check returned opcode + if (command.op_code != returned_opcode) { + std::stringstream msg; + msg << "OpCodes do not match! Sent 0x" << hex << command.op_code << " but received 0x" + << hex << (returned_opcode) << "!"; + throw std::runtime_error(msg.str()); + } + + if(!command.is_read_command) + return "Executed Successfully"; + + std::string rv; + decode_string_from_raw_data(command, cmd_xml.custom_formatters, result.data(), + result.size(), rv, format_type_to_lambda); + return rv; + }catch(std::exception& e){ + return e.what(); + } +} + +extern "C" +JNIEXPORT jbyteArray JNICALL +Java_com_intel_realsense_librealsense_DebugProtocol_nSendAndReceiveRawData(JNIEnv *env, jclass type, + jlong handle, + jbyteArray buffer_) { + jbyte *buffer = env->GetByteArrayElements(buffer_, NULL); + jsize length = env->GetArrayLength(buffer_); + std::vector buff(reinterpret_cast(buffer), reinterpret_cast(buffer) + length); + auto ret = send_and_receive_raw_data(env, reinterpret_cast(handle), buff); + env->ReleaseByteArrayElements(buffer_, buffer, 0); + jbyteArray rv = env->NewByteArray(ret.size()); + env->SetByteArrayRegion (rv, 0, ret.size(), reinterpret_cast(ret.data())); + return rv; +} + +extern "C" +JNIEXPORT jbyteArray JNICALL +Java_com_intel_realsense_librealsense_DebugProtocol_nSendAndReceiveData(JNIEnv *env, jclass clazz, + jlong handle, + jstring filePath, + jstring command) { + const char *file_path = env->GetStringUTFChars(filePath, 0); + const char *line = env->GetStringUTFChars(command, 0); + + std::map format_type_to_lambda; + commands_xml cmd_xml; + + std::string result = "failed to open commands file"; + bool sts = parse_xml_from_file(file_path, cmd_xml); + if(sts){ + update_format_type_to_lambda(format_type_to_lambda); + + result = xml_mode(line, cmd_xml, format_type_to_lambda, env, handle); + } + + env->ReleaseStringUTFChars(command, line); + env->ReleaseStringUTFChars(filePath, file_path); + + jbyteArray rv = env->NewByteArray(result.size()); + env->SetByteArrayRegion(rv, 0, result.size(), + reinterpret_cast(result.c_str())); + return rv; +} + +extern "C" +JNIEXPORT jobjectArray JNICALL +Java_com_intel_realsense_librealsense_DebugProtocol_nGetCommands(JNIEnv *env, jclass clazz, + jstring filePath) { + const char *file_path = env->GetStringUTFChars(filePath, 0); + + jobjectArray rv; + commands_xml cmd_xml; + bool sts = parse_xml_from_file(file_path, cmd_xml); + if(sts) + { + rv = (jobjectArray)env->NewObjectArray(cmd_xml.commands.size(), env->FindClass("java/lang/String"),env->NewStringUTF("")); + int i = 0; + for(auto&& c : cmd_xml.commands) + env->SetObjectArrayElement(rv, i++, env->NewStringUTF(c.first.c_str())); + } + env->ReleaseStringUTFChars(filePath, file_path); + + return(rv); +} + diff --git a/src/android/jni/device.cpp b/src/android/jni/device.cpp index 22c002a2ac..63819473a1 100644 --- a/src/android/jni/device.cpp +++ b/src/android/jni/device.cpp @@ -5,11 +5,15 @@ #include #include #include "error.h" +#include +#include #include "../../../include/librealsense2/rs.h" #include "../../../include/librealsense2/hpp/rs_device.hpp" #include "../../api.h" +using namespace std; + extern "C" JNIEXPORT jboolean JNICALL Java_com_intel_realsense_librealsense_Device_nSupportsInfo(JNIEnv *env, jclass type, jlong handle, jint info) { @@ -59,7 +63,6 @@ Java_com_intel_realsense_librealsense_Device_nQuerySensors(JNIEnv *env, jclass t jlongArray rv = env->NewLongArray(sensors.size()); env->SetLongArrayRegion(rv, 0, sensors.size(), reinterpret_cast(sensors.data())); return rv; - } extern "C" @@ -145,4 +148,4 @@ Java_com_intel_realsense_librealsense_Device_nIsDeviceExtendableTo(JNIEnv *env, static_cast(extension), &e); handle_error(env, e); return rv > 0; -} \ No newline at end of file +} diff --git a/src/android/jni/frame.cpp b/src/android/jni/frame.cpp index 4f4b628a08..558420b7aa 100644 --- a/src/android/jni/frame.cpp +++ b/src/android/jni/frame.cpp @@ -176,3 +176,13 @@ Java_com_intel_realsense_librealsense_Frame_nGetMetadata(JNIEnv *env, jclass typ handle_error(env, e); return rv; } + +extern "C" JNIEXPORT jboolean JNICALL +Java_com_intel_realsense_librealsense_Frame_nSupportsMetadata(JNIEnv *env, jclass type, jlong handle, + jint metadata_type) { + rs2_error *e = NULL; + int rv = rs2_supports_frame_metadata(reinterpret_cast(handle), + static_cast(metadata_type), &e); + handle_error(env, e); + return rv > 0; +} diff --git a/src/android/jni/fw_logger.cpp b/src/android/jni/fw_logger.cpp new file mode 100644 index 0000000000..485d1f5223 --- /dev/null +++ b/src/android/jni/fw_logger.cpp @@ -0,0 +1,28 @@ +// License: Apache 2.0. See LICENSE file in root directory. +// Copyright(c) 2019 Intel Corporation. All Rights Reserved. + +#include +#include + +#include "error.h" +#include "../../../include/librealsense2/rs.h" +#include "../fw-logger/rs-fw-logger.h" + +std::shared_ptr g_fw_logger; +#define TAG "rs_fw_log" + +extern "C" +JNIEXPORT void JNICALL +Java_com_intel_realsense_librealsense_FwLogger_nStartReadingFwLogs(JNIEnv *env, jclass clazz, + jstring file_path) { + const char *filePath = env->GetStringUTFChars(file_path, 0); + g_fw_logger = std::make_shared(filePath); + + env->ReleaseStringUTFChars(file_path, filePath); +} + +extern "C" +JNIEXPORT void JNICALL +Java_com_intel_realsense_librealsense_FwLogger_nStopReadingFwLogs(JNIEnv *env, jclass clazz) { + g_fw_logger.reset(); +} diff --git a/wrappers/android/examples/capture/src/main/java/com/intel/realsense/capture/MainActivity.java b/wrappers/android/examples/capture/src/main/java/com/intel/realsense/capture/MainActivity.java index 1218354663..f58a050005 100644 --- a/wrappers/android/examples/capture/src/main/java/com/intel/realsense/capture/MainActivity.java +++ b/wrappers/android/examples/capture/src/main/java/com/intel/realsense/capture/MainActivity.java @@ -26,7 +26,7 @@ public class MainActivity extends AppCompatActivity { private static final String TAG = "librs capture example"; private static final int PERMISSIONS_REQUEST_CAMERA = 0; - private boolean mPermissionsGrunted = false; + private boolean mPermissionsGranted = false; private Context mAppContext; private TextView mBackGroundText; @@ -60,7 +60,7 @@ protected void onCreate(Bundle savedInstanceState) { return; } - mPermissionsGrunted = true; + mPermissionsGranted = true; } @Override @@ -69,13 +69,13 @@ public void onRequestPermissionsResult(int requestCode, String permissions[], in ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, PERMISSIONS_REQUEST_CAMERA); return; } - mPermissionsGrunted = true; + mPermissionsGranted = true; } @Override protected void onResume() { super.onResume(); - if(mPermissionsGrunted) + if(mPermissionsGranted) init(); else Log.e(TAG, "missing permissions"); diff --git a/wrappers/android/examples/multicam/src/main/java/com/intel/realsense/multicam/MainActivity.java b/wrappers/android/examples/multicam/src/main/java/com/intel/realsense/multicam/MainActivity.java index 8b4de718e7..ad9c5809aa 100644 --- a/wrappers/android/examples/multicam/src/main/java/com/intel/realsense/multicam/MainActivity.java +++ b/wrappers/android/examples/multicam/src/main/java/com/intel/realsense/multicam/MainActivity.java @@ -29,7 +29,7 @@ public class MainActivity extends AppCompatActivity { private static final String TAG = "librs multicam example"; private static final int PERMISSIONS_REQUEST_CAMERA = 0; - private boolean mPermissionsGrunted = false; + private boolean mPermissionsGranted = false; private Context mAppContext; private GLRsSurfaceView mGLSurfaceView; @@ -61,7 +61,7 @@ protected void onCreate(Bundle savedInstanceState) { return; } - mPermissionsGrunted = true; + mPermissionsGranted = true; } @Override @@ -70,13 +70,13 @@ public void onRequestPermissionsResult(int requestCode, String permissions[], in ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, PERMISSIONS_REQUEST_CAMERA); return; } - mPermissionsGrunted = true; + mPermissionsGranted = true; } @Override protected void onResume() { super.onResume(); - if(mPermissionsGrunted) + if(mPermissionsGranted) init(); else Log.e(TAG, "missing permissions"); diff --git a/wrappers/android/examples/processing/src/main/java/com/intel/realsense/processing/MainActivity.java b/wrappers/android/examples/processing/src/main/java/com/intel/realsense/processing/MainActivity.java index e965bae521..7bc2357c24 100644 --- a/wrappers/android/examples/processing/src/main/java/com/intel/realsense/processing/MainActivity.java +++ b/wrappers/android/examples/processing/src/main/java/com/intel/realsense/processing/MainActivity.java @@ -38,7 +38,7 @@ public class MainActivity extends AppCompatActivity { private static final String TAG = "librs process example"; private static final int PERMISSIONS_REQUEST_CAMERA = 0; - private boolean mPermissionsGrunted = false; + private boolean mPermissionsGranted = false; private Context mAppContext; private TextView mBackGroundText; @@ -93,7 +93,7 @@ protected void onCreate(Bundle savedInstanceState) { return; } - mPermissionsGrunted = true; + mPermissionsGranted = true; } @Override @@ -102,13 +102,13 @@ public void onRequestPermissionsResult(int requestCode, String permissions[], in ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, PERMISSIONS_REQUEST_CAMERA); return; } - mPermissionsGrunted = true; + mPermissionsGranted = true; } @Override protected void onResume() { super.onResume(); - if(mPermissionsGrunted) + if(mPermissionsGranted) init(); else Log.e(TAG, "missing permissions"); diff --git a/wrappers/android/examples/recording/src/main/java/com/intel/realsense/recording/MainActivity.java b/wrappers/android/examples/recording/src/main/java/com/intel/realsense/recording/MainActivity.java index bbb0826c53..de5bdda6ca 100644 --- a/wrappers/android/examples/recording/src/main/java/com/intel/realsense/recording/MainActivity.java +++ b/wrappers/android/examples/recording/src/main/java/com/intel/realsense/recording/MainActivity.java @@ -32,7 +32,7 @@ public class MainActivity extends AppCompatActivity { private static final int PERMISSIONS_REQUEST_CAMERA = 0; private static final int PERMISSIONS_REQUEST_WRITE = 1; - private boolean mPermissionsGrunted = false; + private boolean mPermissionsGranted = false; private Context mAppContext; private TextView mBackGroundText; @@ -83,7 +83,7 @@ public void onClick(View view) { return; } - mPermissionsGrunted = true; + mPermissionsGranted = true; } @Override @@ -98,14 +98,14 @@ public void onRequestPermissionsResult(int requestCode, String permissions[], in return; } - mPermissionsGrunted = true; + mPermissionsGranted = true; } @Override protected void onResume() { super.onResume(); - if(mPermissionsGrunted) + if(mPermissionsGranted) init(); else Log.e(TAG, "missing permissions"); diff --git a/wrappers/android/librealsense/src/main/java/com/intel/realsense/librealsense/DebugProtocol.java b/wrappers/android/librealsense/src/main/java/com/intel/realsense/librealsense/DebugProtocol.java new file mode 100644 index 0000000000..5a0c2e0d35 --- /dev/null +++ b/wrappers/android/librealsense/src/main/java/com/intel/realsense/librealsense/DebugProtocol.java @@ -0,0 +1,25 @@ +package com.intel.realsense.librealsense; + +public class DebugProtocol extends Device { + + DebugProtocol(long handle) { + super(handle); + } + + static public String[] getCommands(String commands_file_path){ + return nGetCommands(commands_file_path); + } + + public String SendAndReceiveRawData(String commands_file_path, String command){ + byte[] data = nSendAndReceiveData(mHandle, commands_file_path, command); + return new String(data); + } + + public byte[] SendAndReceiveRawData(byte[] buffer){ + return nSendAndReceiveRawData(mHandle, buffer); + } + + private static native String[] nGetCommands(String filePath); + private static native byte[] nSendAndReceiveData(long handle, String filePath, String command); + private static native byte[] nSendAndReceiveRawData(long handle, byte[] buffer); +} diff --git a/wrappers/android/librealsense/src/main/java/com/intel/realsense/librealsense/Device.java b/wrappers/android/librealsense/src/main/java/com/intel/realsense/librealsense/Device.java index 9a0be414cf..24e658b067 100644 --- a/wrappers/android/librealsense/src/main/java/com/intel/realsense/librealsense/Device.java +++ b/wrappers/android/librealsense/src/main/java/com/intel/realsense/librealsense/Device.java @@ -50,6 +50,7 @@ public T as(Extension extension) { switch (extension){ case UPDATABLE: return (T) new Updatable(mHandle); case UPDATE_DEVICE: return (T) new UpdateDevice(mHandle); + case DEBUG: return (T) new DebugProtocol(mHandle); } throw new RuntimeException("this device is not extendable to " + extension.name()); } diff --git a/wrappers/android/librealsense/src/main/java/com/intel/realsense/librealsense/Frame.java b/wrappers/android/librealsense/src/main/java/com/intel/realsense/librealsense/Frame.java index 739c1ae052..437adced52 100644 --- a/wrappers/android/librealsense/src/main/java/com/intel/realsense/librealsense/Frame.java +++ b/wrappers/android/librealsense/src/main/java/com/intel/realsense/librealsense/Frame.java @@ -36,8 +36,8 @@ public int getNumber(){ return nGetNumber(mHandle); } - public double getTimestamp(){ - return nGetTimestamp(mHandle); + public long getTimestamp(){ + return (long)nGetTimestamp(mHandle); } public TimestampDomain getTimestampDomain() { @@ -45,6 +45,8 @@ public TimestampDomain getTimestampDomain() { return TimestampDomain.values()[rv]; } + public boolean supportsMetadata(FrameMetadata type) { return nSupportsMetadata(mHandle, type.value());} + public long getMetadata(FrameMetadata type) { return nGetMetadata(mHandle, type.value());} public Frame applyFilter(FilterInterface filter) { @@ -79,4 +81,5 @@ public Frame clone() { private static native double nGetTimestamp(long handle); private static native int nGetTimestampDomain(long handle); private static native long nGetMetadata(long handle, int metadata_type); + private static native boolean nSupportsMetadata(long handle, int metadata_type); } diff --git a/wrappers/android/librealsense/src/main/java/com/intel/realsense/librealsense/FwLogger.java b/wrappers/android/librealsense/src/main/java/com/intel/realsense/librealsense/FwLogger.java new file mode 100644 index 0000000000..6ecf6cef0d --- /dev/null +++ b/wrappers/android/librealsense/src/main/java/com/intel/realsense/librealsense/FwLogger.java @@ -0,0 +1,15 @@ +package com.intel.realsense.librealsense; + +public class FwLogger { + + public static void startFwLogging(String filePath) { + nStartReadingFwLogs(filePath); + } + + public static void stopFwLogging() { + nStopReadingFwLogs(); + } + + private static native void nStartReadingFwLogs(String filePath); + private static native void nStopReadingFwLogs(); +} diff --git a/wrappers/android/librealsense/src/main/java/com/intel/realsense/librealsense/Updatable.java b/wrappers/android/librealsense/src/main/java/com/intel/realsense/librealsense/Updatable.java index 884b57c7a0..c3fcf4852d 100644 --- a/wrappers/android/librealsense/src/main/java/com/intel/realsense/librealsense/Updatable.java +++ b/wrappers/android/librealsense/src/main/java/com/intel/realsense/librealsense/Updatable.java @@ -17,6 +17,15 @@ public synchronized byte[] createFlashBackup(ProgressListener listener){ return nCreateFlashBackup(mHandle); } + public synchronized byte[] createFlashBackup(){ + mListener = new ProgressListener() { + @Override + public void onProgress(float progress) { + } + }; + return createFlashBackup(mListener); + } + Updatable(long handle){ super(handle); mOwner = false; diff --git a/wrappers/android/tools/camera/src/main/AndroidManifest.xml b/wrappers/android/tools/camera/src/main/AndroidManifest.xml index d7403858d0..26e03d7956 100644 --- a/wrappers/android/tools/camera/src/main/AndroidManifest.xml +++ b/wrappers/android/tools/camera/src/main/AndroidManifest.xml @@ -44,6 +44,9 @@ + \ No newline at end of file diff --git a/wrappers/android/tools/camera/src/main/java/com/intel/realsense/camera/ControlsDialog.java b/wrappers/android/tools/camera/src/main/java/com/intel/realsense/camera/ControlsDialog.java new file mode 100644 index 0000000000..2bfeb37a43 --- /dev/null +++ b/wrappers/android/tools/camera/src/main/java/com/intel/realsense/camera/ControlsDialog.java @@ -0,0 +1,40 @@ +package com.intel.realsense.camera; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; +import android.os.Bundle; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; + +public class ControlsDialog extends DialogFragment { + + private static final String TAG = "librs controls"; + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + final Activity activity = getActivity(); + + LayoutInflater inflater = activity.getLayoutInflater(); + View fragmentView = inflater.inflate(R.layout.controls_dialog, null); + + View closeButton = fragmentView.findViewById(R.id.controls_close_button); + closeButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + dismissAllowingStateLoss(); + } + }); + + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + builder.setView(fragmentView); + AlertDialog rv = builder.create(); + rv.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); + rv.getWindow().setGravity(Gravity.CENTER_HORIZONTAL | Gravity.TOP); + return rv; + } +} diff --git a/wrappers/android/tools/camera/src/main/java/com/intel/realsense/camera/DetachedActivity.java b/wrappers/android/tools/camera/src/main/java/com/intel/realsense/camera/DetachedActivity.java index 7acd6e577c..e04ed1aa8e 100644 --- a/wrappers/android/tools/camera/src/main/java/com/intel/realsense/camera/DetachedActivity.java +++ b/wrappers/android/tools/camera/src/main/java/com/intel/realsense/camera/DetachedActivity.java @@ -20,9 +20,11 @@ import com.intel.realsense.librealsense.DeviceList; import com.intel.realsense.librealsense.DeviceListener; import com.intel.realsense.librealsense.Extension; +import com.intel.realsense.librealsense.FwLogger; import com.intel.realsense.librealsense.ProductLine; import com.intel.realsense.librealsense.RsContext; +import java.io.File; import java.util.HashMap; import java.util.Map; @@ -32,7 +34,7 @@ public class DetachedActivity extends AppCompatActivity { private static final int PLAYBACK_REQUEST_CODE = 1; private static final String MINIMAL_D400_FW_VERSION = "5.10.0.0"; - private boolean mPermissionsGrunted = false; + private boolean mPermissionsGranted = false; private Button mPlaybackButton; private Context mAppContext; @@ -78,7 +80,7 @@ public void run() { mMinimalFirmwares.put(ProductLine.D400, MINIMAL_D400_FW_VERSION); - mPermissionsGrunted = true; + mPermissionsGranted = true; } @Override @@ -88,14 +90,14 @@ public void onRequestPermissionsResult(int requestCode, String permissions[], in return; } - mPermissionsGrunted = true; + mPermissionsGranted = true; } @Override protected void onResume() { super.onResume(); mDetached = true; - if(mPermissionsGrunted) { + if(mPermissionsGranted) { RsContext.init(getApplicationContext()); mRsContext.setDevicesChangedCallback(mListener); validatedDevice(); @@ -121,6 +123,12 @@ private synchronized void validatedDevice(){ if (!validateFwVersion(d)) return; mDetached = false; + SharedPreferences sharedPref = getSharedPreferences(getString(R.string.app_settings), Context.MODE_PRIVATE); + boolean fw_logging_enabled = sharedPref.getBoolean(getString(R.string.fw_logging), false); + String fw_logging_file_path = sharedPref.getString(getString(R.string.fw_logging_file_path), ""); + if(fw_logging_enabled && !fw_logging_file_path.equals("")){ + FwLogger.startFwLogging(fw_logging_file_path); + } finish(); Intent intent = new Intent(this, PreviewActivity.class); startActivity(intent); diff --git a/wrappers/android/tools/camera/src/main/java/com/intel/realsense/camera/FileBrowserActivity.java b/wrappers/android/tools/camera/src/main/java/com/intel/realsense/camera/FileBrowserActivity.java index 538a5f8120..1053e896bb 100644 --- a/wrappers/android/tools/camera/src/main/java/com/intel/realsense/camera/FileBrowserActivity.java +++ b/wrappers/android/tools/camera/src/main/java/com/intel/realsense/camera/FileBrowserActivity.java @@ -19,7 +19,7 @@ public class FileBrowserActivity extends AppCompatActivity { private static final int PERMISSIONS_REQUEST_READ = 0; - private boolean mPermissionsGrunted = false; + private boolean mPermissionsGranted = false; private String mFolder = ""; @Override protected void onCreate(Bundle savedInstanceState) { @@ -31,7 +31,7 @@ protected void onCreate(Bundle savedInstanceState) { return; } - mPermissionsGrunted = true; + mPermissionsGranted = true; } @Override @@ -41,14 +41,14 @@ public void onRequestPermissionsResult(int requestCode, String permissions[], in return; } - mPermissionsGrunted = true; + mPermissionsGranted = true; } @Override protected void onResume() { super.onResume(); - if(!mPermissionsGrunted) + if(!mPermissionsGranted) return; TextView message = findViewById(R.id.list_view_title); diff --git a/wrappers/android/tools/camera/src/main/java/com/intel/realsense/camera/FileUtilities.java b/wrappers/android/tools/camera/src/main/java/com/intel/realsense/camera/FileUtilities.java new file mode 100644 index 0000000000..fbbecc5f7b --- /dev/null +++ b/wrappers/android/tools/camera/src/main/java/com/intel/realsense/camera/FileUtilities.java @@ -0,0 +1,66 @@ +package com.intel.realsense.camera; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Environment; +import android.os.Handler; +import android.util.Log; + +import java.io.File; +import java.io.FileOutputStream; +import java.nio.file.Path; + +public class FileUtilities { + + private static final String TAG = "file_utilities"; + + public static final int PERMISSIONS_REQUEST_CAMERA = 0; + public static final int PERMISSIONS_REQUEST_READ = 1; + public static final int PERMISSIONS_REQUEST_WRITE = 2; + + private final static Handler mHandler = new Handler(); + private static Thread mUiThread; + + public static final void runOnUiThread(Runnable action) { + if (Thread.currentThread() != mUiThread) { + mHandler.post(action); + } else { + action.run(); + } + } + + public static boolean isExternalStorageWritable() { + return Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED; + } + + public static String getExternalStorageDir() { + return Environment.getExternalStorageDirectory().getAbsolutePath(); + } + + public static void saveFileToExternalDir(final String fileName, byte[] data) { + try { + File file = new File(getExternalStorageDir() + File.separator + fileName); + FileOutputStream fos = new FileOutputStream(file); + fos.write(data); + Log.i(TAG, "saveFileToExternalDir: file " + fileName + "saved successfully"); + } catch (Exception e) { + Log.e(TAG, "saveFileToExternalDir: failed to create a file " + fileName, e); + } + } + + public static boolean isPathEmpty(String path) { + try { + File folder = new File(path); + if (!folder.exists()) { + return true; + } + final File[] files = folder.listFiles(); + if (files.length == 0) + return true; + } catch (Exception e) { + return true; + } + return false; + } +} diff --git a/wrappers/android/tools/camera/src/main/java/com/intel/realsense/camera/PreviewActivity.java b/wrappers/android/tools/camera/src/main/java/com/intel/realsense/camera/PreviewActivity.java index 172356eadd..81492b42b5 100644 --- a/wrappers/android/tools/camera/src/main/java/com/intel/realsense/camera/PreviewActivity.java +++ b/wrappers/android/tools/camera/src/main/java/com/intel/realsense/camera/PreviewActivity.java @@ -13,15 +13,26 @@ import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.RadioButton; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; import com.intel.realsense.librealsense.Config; +import com.intel.realsense.librealsense.Device; +import com.intel.realsense.librealsense.DeviceList; +import com.intel.realsense.librealsense.Frame; +import com.intel.realsense.librealsense.FrameCallback; import com.intel.realsense.librealsense.FrameSet; import com.intel.realsense.librealsense.GLRsSurfaceView; +import com.intel.realsense.librealsense.Option; +import com.intel.realsense.librealsense.RsContext; +import com.intel.realsense.librealsense.Sensor; import java.util.HashMap; +import java.util.List; import java.util.Map; public class PreviewActivity extends AppCompatActivity { @@ -33,6 +44,7 @@ public class PreviewActivity extends AppCompatActivity { private TextView mSettingsButton; private TextView mStatisticsButton; private TextView m3dButton; + private TextView mControlsButton; private TextView mStatsView; private Map mLabels; @@ -70,6 +82,7 @@ protected void onCreate(Bundle savedInstanceState) { mSettingsButton = findViewById(R.id.preview_settings_button); mStatisticsButton = findViewById(R.id.preview_stats_button); m3dButton = findViewById(R.id.preview_3d_button); + mControlsButton = findViewById(R.id.controls_button); mStartRecordFab.setOnClickListener(new View.OnClickListener() { @Override @@ -108,6 +121,14 @@ public void onClick(View view) { editor.commit(); } }); + mControlsButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + ControlsDialog cd = new ControlsDialog(); + cd.setCancelable(true); + cd.show(getFragmentManager(), null); + } + }); mStatisticsButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { @@ -186,7 +207,7 @@ public void config(Config config) { } @Override - public void onFrameset(FrameSet frameSet) { + public void onFrameset(final FrameSet frameSet) { mStreamingStats.onFrameset(frameSet); if(statsToggle){ clearLables(); @@ -230,4 +251,53 @@ protected void onPause() { if(mGLSurfaceView != null) mGLSurfaceView.clear(); } + + public void onRadioButtonClicked(View view) { + boolean checked = ((RadioButton) view).isChecked(); + + if(!checked) + return; + RsContext ctx = new RsContext(); + try(DeviceList devices = ctx.queryDevices()) { + try(Device device = devices.createDevice(0)){ + if(device == null) + return; + List sensors = device.querySensors(); + for(Sensor s : sensors){ + Log.i(TAG, "onRadioButtonClicked: " + s.getValue(Option.HARDWARE_PRESET)); + if(!s.supports(Option.EMITTER_ENABLED)) + continue; + switch(view.getId()) { + case R.id.radio_no_projector:{ + s.setValue(Option.EMITTER_ENABLED, 0); + break; + } + case R.id.radio_laser:{ + s.setValue(Option.EMITTER_ENABLED, 1); + break; + } + case R.id.radio_laser_auto:{ + s.setValue(Option.EMITTER_ENABLED, 2); + break; + } + case R.id.radio_led:{ + s.setValue(Option.EMITTER_ENABLED, 3); + break; + } + case R.id.radio_custom:{ + s.setValue(Option.HARDWARE_PRESET, 0); + break; + } + case R.id.radio_burst:{ + s.setValue(Option.HARDWARE_PRESET, 2); + break; + } + } + } + } catch(Exception e){ + Log.e(TAG, "Failed to set controls: " + e.getMessage()); + Toast.makeText(this, "Failed to set controls", Toast.LENGTH_LONG).show(); + } + } + } } diff --git a/wrappers/android/tools/camera/src/main/java/com/intel/realsense/camera/RecordingActivity.java b/wrappers/android/tools/camera/src/main/java/com/intel/realsense/camera/RecordingActivity.java index 1d073abdad..bc0c435b5e 100644 --- a/wrappers/android/tools/camera/src/main/java/com/intel/realsense/camera/RecordingActivity.java +++ b/wrappers/android/tools/camera/src/main/java/com/intel/realsense/camera/RecordingActivity.java @@ -27,7 +27,7 @@ public class RecordingActivity extends AppCompatActivity { private Streamer mStreamer; private GLRsSurfaceView mGLSurfaceView; - private boolean mPermissionsGrunted = false; + private boolean mPermissionsGranted = false; private FloatingActionButton mStopRecordFab; @@ -54,7 +54,7 @@ public void onClick(View view) { return; } - mPermissionsGrunted = true; + mPermissionsGranted = true; } @Override @@ -64,14 +64,14 @@ public void onRequestPermissionsResult(int requestCode, String permissions[], in return; } - mPermissionsGrunted = true; + mPermissionsGranted = true; } @Override protected void onResume() { super.onResume(); - if(mPermissionsGrunted){ + if(mPermissionsGranted){ mStreamer = new Streamer(this,true, new Streamer.Listener() { @Override public void config(Config config) { diff --git a/wrappers/android/tools/camera/src/main/java/com/intel/realsense/camera/SettingsActivity.java b/wrappers/android/tools/camera/src/main/java/com/intel/realsense/camera/SettingsActivity.java index 61b45ec501..5c1082f813 100644 --- a/wrappers/android/tools/camera/src/main/java/com/intel/realsense/camera/SettingsActivity.java +++ b/wrappers/android/tools/camera/src/main/java/com/intel/realsense/camera/SettingsActivity.java @@ -1,9 +1,17 @@ package com.intel.realsense.camera; +import android.Manifest; +import android.app.ProgressDialog; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.os.AsyncTask; import android.os.Bundle; +import android.support.v4.app.ActivityCompat; +import android.support.v4.content.ContextCompat; +import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.util.Pair; @@ -17,6 +25,7 @@ import com.intel.realsense.librealsense.Device; import com.intel.realsense.librealsense.DeviceList; import com.intel.realsense.librealsense.Extension; +import com.intel.realsense.librealsense.FwLogger; import com.intel.realsense.librealsense.RsContext; import com.intel.realsense.librealsense.Sensor; import com.intel.realsense.librealsense.StreamProfile; @@ -34,20 +43,37 @@ public class SettingsActivity extends AppCompatActivity { private static final String TAG = "librs camera settings"; + AppCompatActivity mContext; + private static final int OPEN_FILE_REQUEST_CODE = 0; + private static final int OPEN_FW_FILE_REQUEST_CODE = 1; private static final int INDEX_DEVICE_INFO = 0; private static final int INDEX_ADVANCE_MODE = 1; private static final int INDEX_PRESETS = 2; private static final int INDEX_UPDATE = 3; private static final int INDEX_UPDATE_UNSIGNED = 4; + private static final int INDEX_TERMINAL = 5; + private static final int INDEX_FW_LOG = 6; + private static final int INDEX_CREATE_FLASH_BACKUP = 7; private Device _device; + private boolean areAdvancedFeaturesEnabled = false; // advanced features (fw logs, terminal etc.) + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_settings); + mContext = this; + + // Advanced features are enabled if xml files exists in the device. + String advancedFeaturesPath = FileUtilities.getExternalStorageDir() + + File.separator + + getString(R.string.realsense_folder) + + File.separator + + "hw"; + areAdvancedFeaturesEnabled = !FileUtilities.isPathEmpty(advancedFeaturesPath); } @Override @@ -113,6 +139,17 @@ private void loadSettingsList(final Device device){ settingsMap.put(INDEX_ADVANCE_MODE,"Disable advanced mode"); settingsMap.put(INDEX_PRESETS,"Presets"); } + + if (areAdvancedFeaturesEnabled) { + SharedPreferences sharedPref = getSharedPreferences(getString(R.string.app_settings), Context.MODE_PRIVATE); + boolean fw_logging_enabled = sharedPref.getBoolean(getString(R.string.fw_logging), false); + settingsMap.put(INDEX_FW_LOG, fw_logging_enabled ? "Stop FW logging" : "Start FW logging"); + + settingsMap.put(INDEX_TERMINAL,"Terminal"); + } + + settingsMap.put(INDEX_CREATE_FLASH_BACKUP, "Create FW backup"); + final String[] settings = new String[settingsMap.values().size()]; settingsMap.values().toArray(settings); final ArrayAdapter adapter = new ArrayAdapter<>(this, R.layout.files_list_view, settings); @@ -124,6 +161,9 @@ private void loadSettingsList(final Device device){ public void onItemClick(AdapterView parent, final View view, int position, long id) { Object[] keys = settingsMap.keySet().toArray(); + if (ContextCompat.checkSelfPermission(mContext, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { + ActivityCompat.requestPermissions(mContext, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, FileUtilities.PERMISSIONS_REQUEST_WRITE); + } switch ((int)keys[position]){ case INDEX_DEVICE_INFO: { Intent intent = new Intent(SettingsActivity.this, InfoActivity.class); @@ -151,6 +191,20 @@ public void onItemClick(AdapterView parent, final View view, startActivityForResult(intent, OPEN_FILE_REQUEST_CODE); break; } + case INDEX_TERMINAL: { + Intent intent = new Intent(SettingsActivity.this, TerminalActivity.class); + startActivity(intent); + break; + } + case INDEX_FW_LOG: { + toggleFwLogging(); + recreate(); + break; + } + case INDEX_CREATE_FLASH_BACKUP: { + new FlashBackupTask(device).execute(); + break; + } default: break; } @@ -158,6 +212,60 @@ public void onItemClick(AdapterView parent, final View view, }); } + private class FlashBackupTask extends AsyncTask { + + private ProgressDialog mProgressDialog; + private Device mDevice; + String mBackupFileName = "fwdump.bin"; + + public FlashBackupTask(Device mDevice) { + this.mDevice = mDevice; + } + + @Override + protected Void doInBackground(Void... voids) { + final Updatable upd = mDevice.as(Extension.UPDATABLE); + FileUtilities.saveFileToExternalDir(mBackupFileName, upd.createFlashBackup()); + return null; + } + + @Override + protected void onPreExecute() { + super.onPreExecute(); + + mProgressDialog = ProgressDialog.show(mContext, "Saving Firmware Backup", "Please wait, this can take a few minutes"); + mProgressDialog.setCanceledOnTouchOutside(false); + mProgressDialog.setCancelable(false); + } + + @Override + protected void onPostExecute(Void aVoid) { + super.onPostExecute(aVoid); + + if (mProgressDialog != null) { + mProgressDialog.dismiss(); + } + + runOnUiThread(new Runnable() { + @Override + public void run() { + new AlertDialog.Builder(mContext) + .setTitle("Firmware Backup Success") + .setMessage("Saved into: " + FileUtilities.getExternalStorageDir() + File.separator + mBackupFileName) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + } + }) + .show(); + + } + }); + } + } + + private static String getDeviceConfig(String pid, StreamType streamType, int streamIndex){ return pid + "_" + streamType.name() + "_" + streamIndex; } @@ -229,24 +337,49 @@ private StreamProfileSelector[] createSettingList(final Device device){ return lines.toArray(new StreamProfileSelector[lines.size()]); } + void toggleFwLogging(){ + SharedPreferences sharedPref = getSharedPreferences(getString(R.string.app_settings), Context.MODE_PRIVATE); + boolean fw_logging_enabled = sharedPref.getBoolean(getString(R.string.fw_logging), false); + String fw_logging_file_path = sharedPref.getString(getString(R.string.fw_logging_file_path), ""); + if(fw_logging_file_path.equals("")){ + Intent intent = new Intent(SettingsActivity.this, FileBrowserActivity.class); + intent.putExtra(getString(R.string.browse_folder), getString(R.string.realsense_folder) + File.separator + "hw"); + startActivityForResult(intent, OPEN_FW_FILE_REQUEST_CODE); + return; + } + if(fw_logging_enabled) + FwLogger.stopFwLogging(); + else + FwLogger.startFwLogging(fw_logging_file_path); + SharedPreferences.Editor editor = sharedPref.edit(); + editor.putBoolean(getString(R.string.fw_logging), !fw_logging_enabled); + editor.commit(); + } + @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); - if (requestCode == OPEN_FILE_REQUEST_CODE && resultCode == RESULT_OK) { - if (data != null) { - String filePath = data.getStringExtra(getString(R.string.intent_extra_file_path)); + if (resultCode != RESULT_OK || data == null) + return; + String filePath = data.getStringExtra(getString(R.string.intent_extra_file_path)); + switch (requestCode){ + case OPEN_FILE_REQUEST_CODE:{ FirmwareUpdateProgressDialog fud = new FirmwareUpdateProgressDialog(); Bundle bundle = new Bundle(); bundle.putString(getString(R.string.firmware_update_file_path), filePath); fud.setArguments(bundle); fud.setCancelable(false); fud.show(getFragmentManager(), null); + break; + } + case OPEN_FW_FILE_REQUEST_CODE: { + SharedPreferences sharedPref = getSharedPreferences(getString(R.string.app_settings), Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sharedPref.edit(); + editor.putString(getString(R.string.fw_logging_file_path), filePath); + editor.commit(); + toggleFwLogging(); + break; } - } - else{ - Intent intent = new Intent(); - setResult(RESULT_OK, intent); - finish(); } } } diff --git a/wrappers/android/tools/camera/src/main/java/com/intel/realsense/camera/StreamingStats.java b/wrappers/android/tools/camera/src/main/java/com/intel/realsense/camera/StreamingStats.java index 6e83e2e7d9..f9e1ee022d 100644 --- a/wrappers/android/tools/camera/src/main/java/com/intel/realsense/camera/StreamingStats.java +++ b/wrappers/android/tools/camera/src/main/java/com/intel/realsense/camera/StreamingStats.java @@ -1,10 +1,14 @@ package com.intel.realsense.camera; +import android.util.Log; + import com.intel.realsense.librealsense.Extension; import com.intel.realsense.librealsense.Frame; import com.intel.realsense.librealsense.FrameCallback; +import com.intel.realsense.librealsense.FrameMetadata; import com.intel.realsense.librealsense.FrameSet; import com.intel.realsense.librealsense.StreamProfile; +import com.intel.realsense.librealsense.TimestampDomain; import com.intel.realsense.librealsense.VideoStreamProfile; import java.util.HashMap; @@ -12,36 +16,67 @@ public class StreamingStats { private static final String TAG = "librs camera streamer"; + private static final String DATA_LOG_TAG = "FRAME_DATA_LOG"; private Map mStreamsMap = new HashMap<>(); - private Map mLastFrames = new HashMap<>(); + private Map mLastFrames = new HashMap<>(); private void initStream(StreamProfile profile){ String resolution = ""; if(profile.is(Extension.VIDEO_PROFILE)){ VideoStreamProfile vsp = profile.as(Extension.VIDEO_PROFILE); - resolution = vsp.getWidth() + "x" + vsp.getHeight() + " | "; + resolution = vsp.getWidth() + "x" + vsp.getHeight(); } - String name = profile.getType().name() + " | " + - profile.getFormat().name() + " | " + - resolution + - profile.getFrameRate(); - mStreamsMap.put(profile.getUniqueId(), new Statistics(name)); - mLastFrames.put(profile.getUniqueId(), 0); + + String streamType = profile.getType().name(); + String format = profile.getFormat().name(); + String res = resolution; + String fps = String.valueOf(profile.getFrameRate()); + + String name = streamType + " | " + + format + " | " + + res + " | " + + fps; + + Statistics stats = new Statistics(name); + stats.mStreamType = streamType; + stats.mFormat = format; + stats.mResolution = res; + stats.mRequestedFps = fps; + + mStreamsMap.put(profile.getUniqueId(), stats); + mLastFrames.put(profile.getUniqueId(), new Statistics(name)); } private FrameCallback mFrameCallback = new FrameCallback() { @Override public void onFrame(Frame f) { try(StreamProfile profile = f.getProfile()) { + int fn = f.getNumber(); + int uid = f.getProfile().getUniqueId(); if (!mLastFrames.containsKey(f.getProfile().getUniqueId())) initStream(profile); - if (mLastFrames.get(f.getProfile().getUniqueId()) != f.getNumber()) { - mLastFrames.put(f.getProfile().getUniqueId(), f.getNumber()); - mStreamsMap.get(f.getProfile().getUniqueId()).onFrame(); + if (mLastFrames.get(uid).mFrameNumber != fn) { + if(f.supportsMetadata(FrameMetadata.FRAME_EMITTER_MODE)) + mStreamsMap.get(uid).mEmitter = String.valueOf(f.getMetadata(FrameMetadata.FRAME_EMITTER_MODE)); + if(f.supportsMetadata(FrameMetadata.ACTUAL_EXPOSURE)) + mStreamsMap.get(uid).mExposure = String.valueOf(f.getMetadata(FrameMetadata.ACTUAL_EXPOSURE)); + if(f.supportsMetadata(FrameMetadata.AUTO_EXPOSURE)) + mStreamsMap.get(uid).mAutoExposureMode = String.valueOf(f.getMetadata(FrameMetadata.AUTO_EXPOSURE)); + if(f.supportsMetadata(FrameMetadata.GAIN_LEVEL)) + mStreamsMap.get(uid).mGain = String.valueOf(f.getMetadata(FrameMetadata.GAIN_LEVEL)); + if(f.supportsMetadata(FrameMetadata.FRAME_LASER_POWER)) + mStreamsMap.get(uid).mLaserPower = String.valueOf(f.getMetadata(FrameMetadata.FRAME_LASER_POWER)); + if(f.supportsMetadata(FrameMetadata.FRAME_LED_POWER)) + mStreamsMap.get(uid).mLedPower = String.valueOf(f.getMetadata(FrameMetadata.FRAME_LED_POWER)); + mStreamsMap.get(uid).mFrameNumber = fn; + mStreamsMap.get(uid).mHWTimestamp = f.getTimestamp(); + mStreamsMap.get(uid).mSWTimestamp = System.currentTimeMillis(); + mStreamsMap.get(uid).onFrame(f); + mLastFrames.put(uid, new Statistics(mStreamsMap.get(uid))); } else - mStreamsMap.get(f.getProfile().getUniqueId()).kick(); + mStreamsMap.get(uid).kick(); } } }; @@ -60,36 +95,86 @@ public String prettyPrint(){ private class Statistics{ private final String mName; - private long mStartTime; - private long mBaseTime; - private float mFps; - private long mFrameCount; - private long mTotalFrameCount; - private long mFirstFrameLatency; - - public void reset(){ - mStartTime = mBaseTime = System.currentTimeMillis(); - mFirstFrameLatency = 0; - } + private String mStreamType; + private String mFormat; + private String mResolution; + private String mRequestedFps; + private long mStartTime = 0; + private long mBaseTime = 0; + private float mFps = 0; + private long mFrameCount = 0; + private long mFrameLoss = 0; + private long mHWTimestamp = 0; + private long mHWTimestampDiff = 0; + private long mSWTimestamp = 0; + private long mSWTimestampDiff = 0; + private int mFrameNumber = 0; + private long mTotalFrameCount = 0; + private long mFirstFrameLatency = 0; + private String mEmitter = "No data"; + private String mExposure = "No data"; + private String mGain = "No data"; + private String mAutoExposureMode = "No data"; + private String mLaserPower = "No data"; + private String mLedPower = "No data"; public Statistics(String name) { mName = name; reset(); } + public Statistics(Statistics other) { + mName = other.mName; + mStreamType = other.mStreamType; + mFormat = other.mFormat; + mResolution = other.mResolution; + mRequestedFps = other.mRequestedFps; + mStartTime = other.mStartTime; + mBaseTime = other.mBaseTime; + mFps = other.mFps; + mFrameCount = other.mFrameCount; + mFrameLoss = other.mFrameLoss; + mHWTimestamp = other.mHWTimestamp; + mHWTimestampDiff = other.mHWTimestampDiff; + mSWTimestamp = other.mSWTimestamp; + mSWTimestampDiff = other.mSWTimestampDiff; + mFrameNumber = other.mFrameNumber; + mTotalFrameCount = other.mTotalFrameCount; + mFirstFrameLatency = other.mFirstFrameLatency; + mEmitter = other.mEmitter; + mExposure = other.mExposure; + mGain = other.mGain; + mAutoExposureMode = other.mAutoExposureMode; + mLaserPower = other.mLaserPower; + mLedPower = other.mLedPower; + } + + public void reset(){ + mStartTime = mBaseTime = System.currentTimeMillis(); + mFirstFrameLatency = 0; + } + public float getFps(){ return mFps; } + public float getFrameCount(){ return mTotalFrameCount; } + public String prettyPrint(){ long curr = System.currentTimeMillis(); int diffInSeconds = (int) ((curr - mStartTime) * 0.001); return mName + "\nFrame Rate: " + mFps + "\nFrame Count: " + mTotalFrameCount + - "\nRun Time: " + diffInSeconds + " [sec]"; + "\nFrame Number: " + mFrameNumber + + "\nFrame Loss: " + mFrameLoss + + "\nHW timestamp: " + mHWTimestamp + + "\nSW timestamp: " + mSWTimestamp + + "\nRun Time: " + diffInSeconds + " [sec]" + + "\nEmitter Mode: " + mEmitter + + "\nExposure: " + mExposure; } public synchronized void kick(){ @@ -102,7 +187,7 @@ public synchronized void kick(){ } } - public synchronized void onFrame(){ + public synchronized void onFrame(Frame f){ mFrameCount++; mTotalFrameCount++; long curr = System.currentTimeMillis(); @@ -110,13 +195,37 @@ public synchronized void onFrame(){ if(mFirstFrameLatency == 0){ mFirstFrameLatency = curr - mBaseTime; } + if(diffInSeconds > 2){ mFps = mFrameCount / diffInSeconds; mFrameCount = 0; mBaseTime = curr; } - } - } + mFrameLoss = mFrameNumber - mTotalFrameCount; + Statistics mLastFrame = mLastFrames.get(f.getProfile().getUniqueId()); + mHWTimestampDiff = mHWTimestamp - mLastFrame.mHWTimestamp; + mSWTimestampDiff = mSWTimestamp - mLastFrame.mSWTimestamp; + } + private void logFrameData() { + String data = + mStreamType + ", " + + mFormat + ", " + + mResolution + ", " + + mRequestedFps + ", " + + mFrameNumber + ", " + + mHWTimestamp + ", " + + mHWTimestampDiff + ", " + + mSWTimestamp + ", " + + mSWTimestampDiff + ", " + + mAutoExposureMode + ", " + + mExposure + ", " + + mGain + ", " + + mLaserPower + ", " + + mEmitter + ", " + + mLedPower; + Log.i(DATA_LOG_TAG, data); + } + } } diff --git a/wrappers/android/tools/camera/src/main/java/com/intel/realsense/camera/TerminalActivity.java b/wrappers/android/tools/camera/src/main/java/com/intel/realsense/camera/TerminalActivity.java new file mode 100644 index 0000000000..92a4cb604f --- /dev/null +++ b/wrappers/android/tools/camera/src/main/java/com/intel/realsense/camera/TerminalActivity.java @@ -0,0 +1,214 @@ +package com.intel.realsense.camera; + +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.util.Log; +import android.view.KeyEvent; +import android.view.View; +import android.widget.ArrayAdapter; +import android.widget.AutoCompleteTextView; +import android.widget.Button; +import android.widget.CompoundButton; +import android.widget.EditText; +import android.widget.Switch; +import android.widget.Toast; + +import com.intel.realsense.librealsense.DebugProtocol; +import com.intel.realsense.librealsense.Device; +import com.intel.realsense.librealsense.DeviceList; +import com.intel.realsense.librealsense.Extension; +import com.intel.realsense.librealsense.FrameSet; +import com.intel.realsense.librealsense.Pipeline; +import com.intel.realsense.librealsense.RsContext; + +import java.io.File; + +public class TerminalActivity extends AppCompatActivity { + + private static final String TAG = "librs camera terminal"; + private static final int OPEN_FILE_REQUEST_CODE = 0; + + private Switch mAutoComplete; + private Switch mStreaming; + + private Button mSendButton; + private Button mClearButton; + private AutoCompleteTextView mInputText; + private EditText mOutputText; + private String mFilePath; + + private Thread mStreamingThread; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_terminal); + + mSendButton = findViewById(R.id.terminal_send_button); + mSendButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + send(); + } + }); + + mClearButton = findViewById(R.id.terminal_clear_button); + mClearButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + mInputText.setText(""); + } + }); + mInputText = findViewById(R.id.terminal_input_edit_text); + mInputText.setOnKeyListener(new View.OnKeyListener() { + public boolean onKey(View v, int keyCode, KeyEvent event) { + if ((event.getAction() == KeyEvent.ACTION_DOWN) && + (keyCode == KeyEvent.KEYCODE_ENTER)) { + send(); + return true; + } + return false; + } + }); + mOutputText = findViewById(R.id.terminal_output_edit_text); + mAutoComplete = findViewById(R.id.terminal_input_auto_complete); + mAutoComplete.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + SharedPreferences sharedPref = getSharedPreferences(getString(R.string.app_settings), Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sharedPref.edit(); + editor.putBoolean(getString(R.string.terminal_auto_complete), isChecked); + editor.commit(); + setupAutoCompleteTextView(); + } + }); + + mStreaming = findViewById(R.id.terminal_stream); + mStreaming.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + if(isChecked) + startStreaming(); + else + stopStreaming(); + } + }); + init(); + } + + private void stopStreaming() { + mStreamingThread.interrupt(); + try { + mStreamingThread.join(1000); + } catch (InterruptedException e) { + Log.e(TAG, "stopStreaming, error: " + e.getMessage()); + } + } + + @Override + protected void onPause() { + super.onPause(); + + if(mStreaming.isChecked()){ + stopStreaming(); + } + } + + private void startStreaming() { + mStreamingThread = new Thread(new Runnable() { + @Override + public void run() { + try(Pipeline pipe = new Pipeline()){ + pipe.start(); + while(!mStreamingThread.isInterrupted()){ + try(FrameSet frames = pipe.waitForFrames()){} + } + pipe.stop(); + } catch (Exception e) { + mOutputText.setText("streaming error: " + e.getMessage()); + } + } + }); + mStreamingThread.start(); + } + + private void send() { + RsContext mRsContext = new RsContext(); + try(DeviceList devices = mRsContext.queryDevices()){ + if(devices.getDeviceCount() == 0) + { + Log.e(TAG, "getDeviceCount returned zero"); + Toast.makeText(this, "Connect a camera", Toast.LENGTH_LONG).show(); + return; + } + try(Device device = devices.createDevice(0)){ + DebugProtocol dp = device.as(Extension.DEBUG); + String content = mInputText.getText().toString(); + String res = dp.SendAndReceiveRawData(mFilePath, content); + mOutputText.setText("command: " + mInputText.getText() + "\n\n" + res); + mInputText.setText(""); + } + catch(Exception e){ + mOutputText.setText("Error: " + e.getMessage()); + } + } + } + + @Override + protected void onResume() { + super.onResume(); + } + + private void setupAutoCompleteTextView(){ + try{ + if(mAutoComplete.isChecked()) { + String[] spArray = DebugProtocol.getCommands(mFilePath); + ArrayAdapter adapter = new ArrayAdapter<>(this, + android.R.layout.simple_dropdown_item_1line, spArray); + mInputText.setAdapter(adapter); + }else{ + mInputText.setAdapter(null); + } + }catch(Exception e){ + Log.e(TAG, e.getMessage()); + mOutputText.setText("Error: " + e.getMessage()); + } + } + + private void init(){ + SharedPreferences sharedPref = getSharedPreferences(getString(R.string.app_settings), Context.MODE_PRIVATE); + mFilePath = sharedPref.getString(getString(R.string.terminal_commands_file), ""); + mAutoComplete.setChecked(sharedPref.getBoolean(getString(R.string.terminal_auto_complete), false)); + + if(mFilePath == null || !(new File(mFilePath).exists())){ + Intent intent = new Intent(this, FileBrowserActivity.class); + intent.putExtra(getString(R.string.browse_folder), getString(R.string.realsense_folder) + File.separator + "hw"); + startActivityForResult(intent, OPEN_FILE_REQUEST_CODE); + } + else + setupAutoCompleteTextView(); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (requestCode == OPEN_FILE_REQUEST_CODE && resultCode == RESULT_OK) { + if (data != null) { + mFilePath = data.getStringExtra(getString(R.string.intent_extra_file_path)); + SharedPreferences sharedPref = getSharedPreferences(getString(R.string.app_settings), Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sharedPref.edit(); + editor.putString(getString(R.string.terminal_commands_file), mFilePath); + editor.commit(); + setupAutoCompleteTextView(); + } + } + else{ + Intent intent = new Intent(); + setResult(RESULT_OK, intent); + finish(); + } + } +} diff --git a/wrappers/android/tools/camera/src/main/res/layout-land/activity_preview.xml b/wrappers/android/tools/camera/src/main/res/layout-land/activity_preview.xml index 9d9c08d4e8..fb734e6031 100644 --- a/wrappers/android/tools/camera/src/main/res/layout-land/activity_preview.xml +++ b/wrappers/android/tools/camera/src/main/res/layout-land/activity_preview.xml @@ -102,15 +102,30 @@ android:layout_height="1dp" android:layout_weight="1" /> - + android:textSize="14dp" + android:textColor="#ffffff" + android:backgroundTint="#000000" + android:text="controls"/> + + + + + \ No newline at end of file diff --git a/wrappers/android/tools/camera/src/main/res/layout/activity_preview.xml b/wrappers/android/tools/camera/src/main/res/layout/activity_preview.xml index 2437140df5..81f7716924 100644 --- a/wrappers/android/tools/camera/src/main/res/layout/activity_preview.xml +++ b/wrappers/android/tools/camera/src/main/res/layout/activity_preview.xml @@ -100,6 +100,21 @@ android:layout_height="1dp" android:layout_weight="1" /> + + + + + \ No newline at end of file diff --git a/wrappers/android/tools/camera/src/main/res/layout/activity_terminal.xml b/wrappers/android/tools/camera/src/main/res/layout/activity_terminal.xml new file mode 100644 index 0000000000..2551a85366 --- /dev/null +++ b/wrappers/android/tools/camera/src/main/res/layout/activity_terminal.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + +