Merge pull request #286 from Microsoft/develop
Merge develop into feature/csharp
This commit is contained in:
Коммит
7cfa67724a
|
@ -32,3 +32,6 @@
|
||||||
[submodule "extern/libjpeg-turbo/src"]
|
[submodule "extern/libjpeg-turbo/src"]
|
||||||
path = extern/libjpeg-turbo/src
|
path = extern/libjpeg-turbo/src
|
||||||
url = https://github.com/libjpeg-turbo/libjpeg-turbo.git
|
url = https://github.com/libjpeg-turbo/libjpeg-turbo.git
|
||||||
|
[submodule "extern/libusb/src"]
|
||||||
|
path = extern/libusb/src
|
||||||
|
url = https://github.com/libusb/libusb
|
||||||
|
|
|
@ -79,8 +79,6 @@ add_subdirectory(tools)
|
||||||
if (K4A_BUILD_DOCS)
|
if (K4A_BUILD_DOCS)
|
||||||
find_package(Doxygen 1.8.14 EXACT)
|
find_package(Doxygen 1.8.14 EXACT)
|
||||||
if (DOXYGEN_FOUND)
|
if (DOXYGEN_FOUND)
|
||||||
set(DOXYGEN_MAINPAGE ${CMAKE_CURRENT_SOURCE_DIR}/docs/sdk.md )
|
|
||||||
|
|
||||||
set(DOXYGEN_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/include/k4a ${CMAKE_CURRENT_SOURCE_DIR}/include/k4arecord ${DOXYGEN_MAINPAGE})
|
set(DOXYGEN_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/include/k4a ${CMAKE_CURRENT_SOURCE_DIR}/include/k4arecord ${DOXYGEN_MAINPAGE})
|
||||||
|
|
||||||
# These variables are used in Doxyfile.in
|
# These variables are used in Doxyfile.in
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
# Code of Conduct
|
||||||
|
|
||||||
|
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
|
||||||
|
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)
|
||||||
|
or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or
|
||||||
|
comments.
|
|
@ -53,7 +53,7 @@ For more instructions on running and writing tests see
|
||||||
[testing](docs/testing.md).
|
[testing](docs/testing.md).
|
||||||
|
|
||||||
# Contribute
|
# Contribute
|
||||||
We welcome your contributions! Please see the [contribution guidelines](docs/contributing.md).
|
We welcome your contributions! Please see the [contribution guidelines](CONTRIBUTING.md).
|
||||||
|
|
||||||
## Feedback
|
## Feedback
|
||||||
For any feedback or to report a bug, please file a [GitHub Issue](https://github.com/Microsoft/Azure-Kinect-Sensor-SDK/issues).
|
For any feedback or to report a bug, please file a [GitHub Issue](https://github.com/Microsoft/Azure-Kinect-Sensor-SDK/issues).
|
||||||
|
|
|
@ -7,9 +7,9 @@ variables:
|
||||||
- name: 'skipComponentGovernaceDetection'
|
- name: 'skipComponentGovernaceDetection'
|
||||||
value: true
|
value: true
|
||||||
- name: 'linux_firmware_version'
|
- name: 'linux_firmware_version'
|
||||||
value: '1.5.946614'
|
value: '1.6.987014'
|
||||||
- name: 'windows_firmware_version'
|
- name: 'windows_firmware_version'
|
||||||
value: '1.5.926614'
|
value: '1.6.987014'
|
||||||
|
|
||||||
trigger:
|
trigger:
|
||||||
batch: false
|
batch: false
|
||||||
|
@ -490,7 +490,7 @@ jobs:
|
||||||
command: download
|
command: download
|
||||||
vstsFeed: 'DepthEnginePlugin'
|
vstsFeed: 'DepthEnginePlugin'
|
||||||
vstsFeedPackage: 'depthengine'
|
vstsFeedPackage: 'depthengine'
|
||||||
vstsPackageVersion: "1.0.1"
|
vstsPackageVersion: "1.0.5"
|
||||||
downloadDirectory: "$(System.ArtifactsDirectory)/depthengineplugin"
|
downloadDirectory: "$(System.ArtifactsDirectory)/depthengineplugin"
|
||||||
|
|
||||||
- task: CopyFiles@2
|
- task: CopyFiles@2
|
||||||
|
@ -516,7 +516,7 @@ jobs:
|
||||||
filePath: './scripts/Reset-Device.ps1'
|
filePath: './scripts/Reset-Device.ps1'
|
||||||
displayName: 'Reset K4A Device'
|
displayName: 'Reset K4A Device'
|
||||||
|
|
||||||
- script: '.\amd64-windows-msvc-RelWithDebInfo\bin\AzureKinectFirmwareTool.exe -u firmware/AzureKinect_Fw_$(firmware_version).bin'
|
- script: '.\amd64-windows-msvc-RelWithDebInfo\bin\AzureKinectFirmwareTool.exe -u firmware/AzureKinectDK_Fw_$(firmware_version).bin'
|
||||||
workingDirectory: '$(System.ArtifactsDirectory)'
|
workingDirectory: '$(System.ArtifactsDirectory)'
|
||||||
displayName: 'Update Device'
|
displayName: 'Update Device'
|
||||||
|
|
||||||
|
@ -582,7 +582,7 @@ jobs:
|
||||||
command: download
|
command: download
|
||||||
vstsFeed: 'DepthEnginePlugin'
|
vstsFeed: 'DepthEnginePlugin'
|
||||||
vstsFeedPackage: 'depthengine'
|
vstsFeedPackage: 'depthengine'
|
||||||
vstsPackageVersion: "1.0.1"
|
vstsPackageVersion: "1.0.5"
|
||||||
downloadDirectory: "$(System.ArtifactsDirectory)/depthengineplugin"
|
downloadDirectory: "$(System.ArtifactsDirectory)/depthengineplugin"
|
||||||
|
|
||||||
- task: UniversalPackages@0
|
- task: UniversalPackages@0
|
||||||
|
@ -634,7 +634,7 @@ jobs:
|
||||||
workingDirectory: '$(System.ArtifactsDirectory)/findconnectedport/linux/'
|
workingDirectory: '$(System.ArtifactsDirectory)/findconnectedport/linux/'
|
||||||
displayName: 'Reset K4A Device'
|
displayName: 'Reset K4A Device'
|
||||||
|
|
||||||
- script: './x86_64-linux-clang-relwithdebinfo/bin/AzureKinectFirmwareTool -u firmware/AzureKinect_Fw_$(firmware_version).bin'
|
- script: './x86_64-linux-clang-relwithdebinfo/bin/AzureKinectFirmwareTool -u firmware/AzureKinectDK_Fw_$(firmware_version).bin'
|
||||||
workingDirectory: '$(System.ArtifactsDirectory)'
|
workingDirectory: '$(System.ArtifactsDirectory)'
|
||||||
displayName: 'Update Device'
|
displayName: 'Update Device'
|
||||||
|
|
||||||
|
|
|
@ -1,119 +0,0 @@
|
||||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
# Licensed under the MIT License.
|
|
||||||
|
|
||||||
# FindLibUSB.cmake
|
|
||||||
#
|
|
||||||
# Cross platform module to find the libusb library
|
|
||||||
#
|
|
||||||
# This will define the following variables
|
|
||||||
#
|
|
||||||
# LibUSB_FOUND
|
|
||||||
#
|
|
||||||
# and the following imported targets
|
|
||||||
#
|
|
||||||
# LibUSB::LibUSB
|
|
||||||
|
|
||||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
|
|
||||||
|
|
||||||
set(LIBUSB_PREBUILT_WIN_LIB_URL https://github.com/libusb/libusb/releases/download/v1.0.22/libusb-1.0.22.7z)
|
|
||||||
get_filename_component(LIBUSB_7ZIP_FILENAME ${LIBUSB_PREBUILT_WIN_LIB_URL} NAME)
|
|
||||||
set(LIBUSB_7ZIP ${CMAKE_CURRENT_BINARY_DIR}/${LIBUSB_7ZIP_FILENAME})
|
|
||||||
|
|
||||||
# For Windows builds, download pre-built libusb libraries
|
|
||||||
include(FetchContent)
|
|
||||||
FetchContent_Declare(
|
|
||||||
libusb_prebuilt_win_lib
|
|
||||||
URL ${LIBUSB_PREBUILT_WIN_LIB_URL}
|
|
||||||
URL_HASH MD5=750E64B45ACA94FAFBDFF07171004D03
|
|
||||||
)
|
|
||||||
|
|
||||||
FetchContent_GetProperties(libusb_prebuilt_win_lib)
|
|
||||||
if (NOT libusb_prebuilt_win_lib_POPULATED)
|
|
||||||
FetchContent_Populate(
|
|
||||||
libusb_prebuilt_win_lib
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Check for path to include directory
|
|
||||||
find_path(LIBUSB_INCLUDE_DIR
|
|
||||||
NAMES
|
|
||||||
libusb.h
|
|
||||||
PATHS
|
|
||||||
${libusb_prebuilt_win_lib_SOURCE_DIR}/include
|
|
||||||
PATH_SUFFIXES
|
|
||||||
libusb-1.0
|
|
||||||
)
|
|
||||||
|
|
||||||
if ("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4")
|
|
||||||
set(LIBUSB_LIB_PATH "${libusb_prebuilt_win_lib_SOURCE_DIR}/MS32/dll")
|
|
||||||
elseif ("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8")
|
|
||||||
set(LIBUSB_LIB_PATH "${libusb_prebuilt_win_lib_SOURCE_DIR}/MS64/dll")
|
|
||||||
else()
|
|
||||||
message(FATAL_ERROR "Unsupported size of void ptr: ${CMAKE_SIZEOF_VOID_P}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
find_library(LIBUSB_LIB
|
|
||||||
NAMES
|
|
||||||
libusb-1.0
|
|
||||||
PATHS
|
|
||||||
${LIBUSB_LIB_PATH}
|
|
||||||
)
|
|
||||||
|
|
||||||
if ("${LIBUSB_LIB}" STREQUAL "LIBUSB_LIB-NOTFOUND")
|
|
||||||
message(FATAL_ERROR "LibUSB not found")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
else()
|
|
||||||
# For linux builds, find installed libusb libraries
|
|
||||||
find_package(PkgConfig REQUIRED)
|
|
||||||
pkg_check_modules(PC_LibUSB REQUIRED "libusb-1.0")
|
|
||||||
|
|
||||||
find_path(LIBUSB_INCLUDE_DIR
|
|
||||||
NAMES
|
|
||||||
libusb.h
|
|
||||||
HINTS
|
|
||||||
${PC_LibUSB_INCLUDE_DIRS}
|
|
||||||
PATH_SUFFIXES
|
|
||||||
libusb-1.0
|
|
||||||
)
|
|
||||||
|
|
||||||
find_library(LIBUSB_LIB
|
|
||||||
NAMES
|
|
||||||
${PC_LibUSB_LIBRARIES}
|
|
||||||
HINTS
|
|
||||||
${PC_LibUSB_LIBDIR}
|
|
||||||
${PC_LibUSB_LIBRARY_DIRS}
|
|
||||||
)
|
|
||||||
|
|
||||||
endif()
|
|
||||||
|
|
||||||
mark_as_advanced(LIBUSB_INCLUDE_DIR)
|
|
||||||
mark_as_advanced(LIBUSB_LIB)
|
|
||||||
|
|
||||||
include(FindPackageHandleStandardArgs)
|
|
||||||
find_package_handle_standard_args(LibUSB DEFAULT_MSG LIBUSB_INCLUDE_DIR LIBUSB_LIB)
|
|
||||||
|
|
||||||
if (LibUSB_FOUND AND NOT TARGET LibUSB::LibUSB)
|
|
||||||
add_library(LibUSB::LibUSB SHARED IMPORTED)
|
|
||||||
|
|
||||||
# On Windows copy the libUSB dll to the bin directory
|
|
||||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
|
|
||||||
# Use REGEX instead of NAME_WE since LibUsb-1.0.lib considers the extension "0.lib"
|
|
||||||
string(REGEX REPLACE "^.*/([^/]*)\\.lib$" "\\1" LibUSB_NAME ${LIBUSB_LIB})
|
|
||||||
get_filename_component(LibUSB_DIRECTORY ${LIBUSB_LIB} DIRECTORY)
|
|
||||||
set(LibUSB_SHARED_PATH ${LibUSB_DIRECTORY}/${LibUSB_NAME}.dll)
|
|
||||||
else(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
|
|
||||||
set(LibUSB_SHARED_PATH ${LIBUSB_LIB})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
|
|
||||||
set_target_properties(LibUSB::LibUSB PROPERTIES
|
|
||||||
INTERFACE_INCLUDE_DIRECTORIES "${LIBUSB_INCLUDE_DIR}"
|
|
||||||
IMPORTED_IMPLIB "${LIBUSB_LIB}"
|
|
||||||
IMPORTED_LOCATION "${LibUSB_SHARED_PATH}")
|
|
||||||
|
|
||||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
|
|
||||||
include(CopyImportedBinary)
|
|
||||||
copy_imported_binary(TARGET LibUSB::LibUSB)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
|
@ -24,6 +24,7 @@ if ("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
|
||||||
list(APPEND CLANG_ALL_WARNINGS "-Wno-documentation-unknown-command") # Allow undocumented documentation commands used by doxygen
|
list(APPEND CLANG_ALL_WARNINGS "-Wno-documentation-unknown-command") # Allow undocumented documentation commands used by doxygen
|
||||||
list(APPEND CLANG_ALL_WARNINGS "-Wno-covered-switch-default") # Allow default: in switch statements that cover all enum values
|
list(APPEND CLANG_ALL_WARNINGS "-Wno-covered-switch-default") # Allow default: in switch statements that cover all enum values
|
||||||
list(APPEND CLANG_ALL_WARNINGS "-Wno-unreachable-code-break") # Allow break even if it is unreachable
|
list(APPEND CLANG_ALL_WARNINGS "-Wno-unreachable-code-break") # Allow break even if it is unreachable
|
||||||
|
list(APPEND CLANG_ALL_WARNINGS "-Wno-double-promotion") # Allow floats to be promoted to doubles. Needed for isnan() on some systems
|
||||||
if (NOT (${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS "5.0.0"))
|
if (NOT (${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS "5.0.0"))
|
||||||
# Added in clang 5
|
# Added in clang 5
|
||||||
list(APPEND CLANG_ALL_WARNINGS "-Wno-zero-as-null-pointer-constant") # Allow zero as nullptr
|
list(APPEND CLANG_ALL_WARNINGS "-Wno-zero-as-null-pointer-constant") # Allow zero as nullptr
|
||||||
|
@ -46,4 +47,4 @@ elseif ("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC")
|
||||||
add_compile_options(${MSVC_WARNINGS_AS_ERRORS})
|
add_compile_options(${MSVC_WARNINGS_AS_ERRORS})
|
||||||
else()
|
else()
|
||||||
message(FATAL_ERROR "Unknown C++ compiler: ${CMAKE_CXX_COMPILER_ID}")
|
message(FATAL_ERROR "Unknown C++ compiler: ${CMAKE_CXX_COMPILER_ID}")
|
||||||
endif()
|
endif()
|
||||||
|
|
Двоичные данные
docs/Architecture.png
Двоичные данные
docs/Architecture.png
Двоичный файл не отображается.
До Ширина: | Высота: | Размер: 65 KiB |
Двоичные данные
docs/SDK Internal Architecture.vsdx
Двоичные данные
docs/SDK Internal Architecture.vsdx
Двоичный файл не отображается.
60
docs/sdk.md
60
docs/sdk.md
|
@ -1,60 +0,0 @@
|
||||||
# Kinect for Azure SDK
|
|
||||||
|
|
||||||
# Architecture
|
|
||||||
![alt text](Architecture.png)
|
|
||||||
|
|
||||||
Project Kinect for Azure is a sensor that contains a depth camera, color camera, an imu, and audio mic array. The SDK
|
|
||||||
provides interfaces to the depth, color, and IMU sensors. Audio and parts of the color sensor will be routed through
|
|
||||||
system primitive interfaces so that they may be used without the need for the SDK to be running.
|
|
||||||
|
|
||||||
|
|
||||||
The SDK is broken into a modular design that allows individual components to be tested in isolation before being
|
|
||||||
integrated into the system.
|
|
||||||
|
|
||||||
|
|
||||||
# Construction of the K4A SDK
|
|
||||||
The user will open and initialize the k4a SDK by calling k4a_device_open. The library will create:
|
|
||||||
1. depth_mcu
|
|
||||||
1. color_mcu
|
|
||||||
|
|
||||||
These two modules will create USB command (CMD) modules that are responsible for interfaceing with LIBUSB and
|
|
||||||
communincating with the hardware.
|
|
||||||
|
|
||||||
|
|
||||||
Next the library will create the calibration module, where calibration data will be stored and extrinsic data can be
|
|
||||||
convereted to offsets between sensors (as 1 example).
|
|
||||||
|
|
||||||
|
|
||||||
Once the calibration module has been created, the k4a library will create the following modules:
|
|
||||||
1. depth
|
|
||||||
1. color
|
|
||||||
1. imu
|
|
||||||
|
|
||||||
When creating these modules, they will recieve a handle to calibration, depth_mcu, and color_mcu so that the they can
|
|
||||||
communicate with the necessary modules.
|
|
||||||
|
|
||||||
|
|
||||||
Finally the K4A library will create the SDK API module and pass it calibration, color, depth, and imu handles to support
|
|
||||||
the public interfaces.
|
|
||||||
|
|
||||||
|
|
||||||
The creation of the 'correlated captures' module is on demand when correlated data is needed.
|
|
||||||
|
|
||||||
# Data flow and threads
|
|
||||||
|
|
||||||
The SDK is designed such that the caller owns most of the threads responsible for accessing the sensor. API calls to
|
|
||||||
configure, start, stop, and fetch capture data are all designed for the the user to call into the SDK and block if neccessary.
|
|
||||||
|
|
||||||
|
|
||||||
The 'usb_cmd' modules are an exception to this as they require a dedicate thread to keep LibUsb filled with buffers so
|
|
||||||
that we can stream depth and imu data to the SDK. (The color sensor will also likely have a thread. That part of the
|
|
||||||
design is still TBD.) The 'usb_cmd' thread will call a callback function to depth or imu. In the case of the depth
|
|
||||||
callback function for streamed date, the data will be briefly sent to the 'r2d' (raw to depth) module so the raw
|
|
||||||
depth capture can be converted to depth point map. Once the 'r2d' is done with the convertion the sample will be given to
|
|
||||||
the queue. If the user has called an API like k4a_device_get_capture on an empty queue, then the users thread will be
|
|
||||||
unblocked and allowed to return the newly provided capture.
|
|
||||||
|
|
||||||
|
|
||||||
If the user has configured the SDK for correlated captures, then the depth and color callback functions will also provide
|
|
||||||
the respective samples to the 'correlated captures' module.
|
|
||||||
|
|
|
@ -50,8 +50,8 @@ can be run using "ctest -L perf" in the build directory.
|
||||||
Firmware tests are used to validate new firmware drops. They are run manually
|
Firmware tests are used to validate new firmware drops. They are run manually
|
||||||
when a new firmware candidate is given and will require hardware. Firmware
|
when a new firmware candidate is given and will require hardware. Firmware
|
||||||
tests are built using the GoogleTest framework. After compiling, firmware tests
|
tests are built using the GoogleTest framework. After compiling, firmware tests
|
||||||
can be run using "bin\firmware_fw.exe --firmware \<firmware path\>" in the build
|
can be run using "bin\firmware_fw.exe -ff \<factory firmware\> -lf \<lkg firmware\>
|
||||||
directory.
|
-tf \<test firmware\> -cf \<candidate firmware\>" in the build directory.
|
||||||
|
|
||||||
## Running tests
|
## Running tests
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
add_subdirectory(calibration)
|
add_subdirectory(calibration)
|
||||||
add_subdirectory(enumerate)
|
add_subdirectory(enumerate)
|
||||||
|
add_subdirectory(playback_external_sync)
|
||||||
add_subdirectory(fastpointcloud)
|
add_subdirectory(fastpointcloud)
|
||||||
add_subdirectory(k4arecorder)
|
add_subdirectory(k4arecorder)
|
||||||
add_subdirectory(opencv_compatibility)
|
add_subdirectory(opencv_compatibility)
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
# Licensed under the MIT License.
|
||||||
|
|
||||||
|
add_executable(playback_external_sync main.c)
|
||||||
|
target_link_libraries(playback_external_sync
|
||||||
|
k4a::k4a
|
||||||
|
k4a::k4arecord
|
||||||
|
)
|
|
@ -0,0 +1,204 @@
|
||||||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <k4a/k4a.h>
|
||||||
|
#include <k4arecord/playback.h>
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
char *filename;
|
||||||
|
k4a_playback_t handle;
|
||||||
|
k4a_record_configuration_t record_config;
|
||||||
|
k4a_capture_t capture;
|
||||||
|
} recording_t;
|
||||||
|
|
||||||
|
static uint64_t first_capture_timestamp(k4a_capture_t capture)
|
||||||
|
{
|
||||||
|
uint64_t min_timestamp = (uint64_t)-1;
|
||||||
|
k4a_image_t images[3];
|
||||||
|
images[0] = k4a_capture_get_color_image(capture);
|
||||||
|
images[1] = k4a_capture_get_depth_image(capture);
|
||||||
|
images[2] = k4a_capture_get_ir_image(capture);
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
if (images[i] != NULL)
|
||||||
|
{
|
||||||
|
uint64_t timestamp = k4a_image_get_timestamp_usec(images[i]);
|
||||||
|
if (timestamp < min_timestamp)
|
||||||
|
{
|
||||||
|
min_timestamp = timestamp;
|
||||||
|
}
|
||||||
|
k4a_image_release(images[i]);
|
||||||
|
images[i] = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return min_timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_capture_info(recording_t *file)
|
||||||
|
{
|
||||||
|
k4a_image_t images[3];
|
||||||
|
images[0] = k4a_capture_get_color_image(file->capture);
|
||||||
|
images[1] = k4a_capture_get_depth_image(file->capture);
|
||||||
|
images[2] = k4a_capture_get_ir_image(file->capture);
|
||||||
|
|
||||||
|
printf("%-32s", file->filename);
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
if (images[i] != NULL)
|
||||||
|
{
|
||||||
|
uint64_t timestamp = k4a_image_get_timestamp_usec(images[i]) +
|
||||||
|
(uint64_t)file->record_config.start_timestamp_offset_usec;
|
||||||
|
printf(" %7ju usec", timestamp);
|
||||||
|
k4a_image_release(images[i]);
|
||||||
|
images[i] = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf(" %12s", "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
if (argc < 3)
|
||||||
|
{
|
||||||
|
printf("Usage: playback_external_sync.exe <master.mkv> <sub1.mkv>...\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t file_count = (size_t)(argc - 1);
|
||||||
|
bool master_found = false;
|
||||||
|
k4a_result_t result = K4A_RESULT_SUCCEEDED;
|
||||||
|
|
||||||
|
// Allocate memory to store the state of N recordings.
|
||||||
|
recording_t *files = malloc(sizeof(recording_t) * file_count);
|
||||||
|
if (files == NULL)
|
||||||
|
{
|
||||||
|
printf("Failed to allocate memory for playback (%zu bytes)\n", sizeof(recording_t) * file_count);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
memset(files, 0, sizeof(recording_t) * file_count);
|
||||||
|
|
||||||
|
// Open each recording file and validate they were recorded in master/subordinate mode.
|
||||||
|
for (size_t i = 0; i < file_count; i++)
|
||||||
|
{
|
||||||
|
files[i].filename = argv[i + 1];
|
||||||
|
|
||||||
|
result = k4a_playback_open(files[i].filename, &files[i].handle);
|
||||||
|
if (result != K4A_RESULT_SUCCEEDED)
|
||||||
|
{
|
||||||
|
printf("Failed to open file: %s\n", files[i].filename);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = k4a_playback_get_record_configuration(files[i].handle, &files[i].record_config);
|
||||||
|
if (result != K4A_RESULT_SUCCEEDED)
|
||||||
|
{
|
||||||
|
printf("Failed to get record configuration for file: %s\n", files[i].filename);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (files[i].record_config.wired_sync_mode == K4A_WIRED_SYNC_MODE_MASTER)
|
||||||
|
{
|
||||||
|
printf("Opened master recording file: %s\n", files[i].filename);
|
||||||
|
if (master_found)
|
||||||
|
{
|
||||||
|
printf("ERROR: Multiple master recordings listed!\n");
|
||||||
|
result = K4A_RESULT_FAILED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
master_found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (files[i].record_config.wired_sync_mode == K4A_WIRED_SYNC_MODE_SUBORDINATE)
|
||||||
|
{
|
||||||
|
printf("Opened subordinate recording file: %s\n", files[i].filename);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("ERROR: Recording file was not recorded in master/sub mode: %s\n", files[i].filename);
|
||||||
|
result = K4A_RESULT_FAILED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the first capture of each recording into memory.
|
||||||
|
k4a_stream_result_t stream_result = k4a_playback_get_next_capture(files[i].handle, &files[i].capture);
|
||||||
|
if (stream_result == K4A_STREAM_RESULT_EOF)
|
||||||
|
{
|
||||||
|
printf("ERROR: Recording file is empty: %s\n", files[i].filename);
|
||||||
|
result = K4A_RESULT_FAILED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (stream_result == K4A_STREAM_RESULT_FAILED)
|
||||||
|
{
|
||||||
|
printf("ERROR: Failed to read first capture from file: %s\n", files[i].filename);
|
||||||
|
result = K4A_RESULT_FAILED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result == K4A_RESULT_SUCCEEDED)
|
||||||
|
{
|
||||||
|
printf("%-32s %12s %12s %12s\n", "Source file", "COLOR", "DEPTH", "IR");
|
||||||
|
printf("==========================================================================\n");
|
||||||
|
|
||||||
|
// Print the first 25 captures in order of timestamp across all the recordings.
|
||||||
|
for (int frame = 0; frame < 25; frame++)
|
||||||
|
{
|
||||||
|
uint64_t min_timestamp = (uint64_t)-1;
|
||||||
|
recording_t *min_file = NULL;
|
||||||
|
|
||||||
|
// Find the lowest timestamp out of each of the current captures.
|
||||||
|
for (size_t i = 0; i < file_count; i++)
|
||||||
|
{
|
||||||
|
if (files[i].capture != NULL)
|
||||||
|
{
|
||||||
|
// All recording files start at timestamp 0, however the first timestamp off the camera is usually
|
||||||
|
// non-zero. We need to add the recording "start offset" back to the recording timestamp to recover
|
||||||
|
// the original timestamp from the device, and synchronize the files.
|
||||||
|
uint64_t timestamp = first_capture_timestamp(files[i].capture) +
|
||||||
|
files[i].record_config.start_timestamp_offset_usec;
|
||||||
|
if (timestamp < min_timestamp)
|
||||||
|
{
|
||||||
|
min_timestamp = timestamp;
|
||||||
|
min_file = &files[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print_capture_info(min_file);
|
||||||
|
|
||||||
|
k4a_capture_release(min_file->capture);
|
||||||
|
min_file->capture = NULL;
|
||||||
|
|
||||||
|
// Advance the recording with the lowest current timestamp forward.
|
||||||
|
k4a_stream_result_t stream_result = k4a_playback_get_next_capture(min_file->handle, &min_file->capture);
|
||||||
|
if (stream_result == K4A_STREAM_RESULT_FAILED)
|
||||||
|
{
|
||||||
|
printf("ERROR: Failed to read next capture from file: %s\n", min_file->filename);
|
||||||
|
result = K4A_RESULT_FAILED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < file_count; i++)
|
||||||
|
{
|
||||||
|
if (files[i].handle != NULL)
|
||||||
|
{
|
||||||
|
k4a_playback_close(files[i].handle);
|
||||||
|
files[i].handle = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(files);
|
||||||
|
return result == K4A_RESULT_SUCCEEDED ? 0 : 1;
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
# Playback External Sync example
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
The external sync playback example shows how to open recordings generated from multiple cameras in external sync mode, and access the individual frames from each camera in a synchronized manner.
|
||||||
|
|
||||||
|
## Usage Info
|
||||||
|
|
||||||
|
playback_external_sync.exe <master.mkv> <sub1.mkv> <sub2.mkv>...
|
|
@ -29,7 +29,6 @@
|
||||||
#pragma clang diagnostic push
|
#pragma clang diagnostic push
|
||||||
#pragma clang diagnostic ignored "-Wdocumentation"
|
#pragma clang diagnostic ignored "-Wdocumentation"
|
||||||
#pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
|
#pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
|
||||||
#pragma clang diagnostic ignored "-Wdouble-promotion"
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <GL/gl3w.h>
|
#include <GL/gl3w.h>
|
||||||
|
|
|
@ -1,12 +1,45 @@
|
||||||
find_package(LibUSB REQUIRED)
|
add_library(LibUSB
|
||||||
|
STATIC
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/src/libusb/core.c"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/src/libusb/descriptor.c"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/src/libusb/hotplug.c"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/src/libusb/io.c"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/src/libusb/strerror.c"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/src/libusb/sync.c")
|
||||||
|
|
||||||
|
target_include_directories(LibUSB PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src/libusb)
|
||||||
|
target_include_directories(LibUSB PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/libusb/os)
|
||||||
|
|
||||||
|
add_library(LibUSB::LibUSB ALIAS LibUSB)
|
||||||
|
|
||||||
|
if ("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
|
||||||
|
target_compile_options(LibUSB PUBLIC "-Wno-zero-length-array")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Windows")
|
if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Windows")
|
||||||
# Setup install
|
target_sources(LibUSB PRIVATE
|
||||||
include(GNUInstallDirs)
|
"${CMAKE_CURRENT_SOURCE_DIR}/src/libusb/os/poll_windows.c"
|
||||||
install(
|
"${CMAKE_CURRENT_SOURCE_DIR}/src/libusb/os/threads_windows.c"
|
||||||
FILES
|
"${CMAKE_CURRENT_SOURCE_DIR}/src/libusb/os/windows_nt_common.c"
|
||||||
$<TARGET_FILE:LibUSB::LibUSB>
|
"${CMAKE_CURRENT_SOURCE_DIR}/src/libusb/os/windows_usbdk.c"
|
||||||
DESTINATION
|
"${CMAKE_CURRENT_SOURCE_DIR}/src/libusb/os/windows_winusb.c")
|
||||||
${CMAKE_INSTALL_BINDIR}
|
|
||||||
)
|
target_include_directories(LibUSB PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/msvc)
|
||||||
endif()
|
|
||||||
|
target_compile_definitions(LibUSB PRIVATE "_LIB" "_CRT_SECURE_NO_WARNINGS" "WINVER=0x0501" "_WIN32_WINNT=0x0501")
|
||||||
|
|
||||||
|
elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
|
||||||
|
target_sources(LibUSB PRIVATE
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/src/libusb/os/poll_posix.c"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/src/libusb/os/threads_posix.c"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/src/libusb/os/linux_usbfs.c"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/src/libusb/os/linux_udev.c")
|
||||||
|
|
||||||
|
target_include_directories(LibUSB PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/)
|
||||||
|
|
||||||
|
target_link_libraries(LibUSB PUBLIC "udev")
|
||||||
|
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Unknown CMAKE_SYSTEM_NAME: ${CMAKE_SYSTEM_NAME}")
|
||||||
|
endif()
|
||||||
|
|
|
@ -0,0 +1,162 @@
|
||||||
|
/* config.h. THIS WAS GENERATED FROM configure and autoheader and modified to be used in Linux builds */
|
||||||
|
|
||||||
|
/* config.h. Generated from config.h.in by configure. */
|
||||||
|
/* config.h.in. Generated from configure.ac by autoheader. */
|
||||||
|
|
||||||
|
/* Default visibility */
|
||||||
|
#define DEFAULT_VISIBILITY __attribute__((visibility("default")))
|
||||||
|
|
||||||
|
/* Start with debug message logging enabled */
|
||||||
|
/* #undef ENABLE_DEBUG_LOGGING */
|
||||||
|
|
||||||
|
/* Message logging */
|
||||||
|
#define ENABLE_LOGGING 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <asm/types.h> header file. */
|
||||||
|
/* #undef HAVE_ASM_TYPES_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the declaration of `TFD_CLOEXEC', and to 0 if you
|
||||||
|
don't. */
|
||||||
|
#define HAVE_DECL_TFD_CLOEXEC 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the declaration of `TFD_NONBLOCK', and to 0 if you
|
||||||
|
don't. */
|
||||||
|
#define HAVE_DECL_TFD_NONBLOCK 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <dlfcn.h> header file. */
|
||||||
|
#define HAVE_DLFCN_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||||
|
#define HAVE_INTTYPES_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `udev' library (-ludev). */
|
||||||
|
#define HAVE_LIBUDEV 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <libudev.h> header file. */
|
||||||
|
#define HAVE_LIBUDEV_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <linux/netlink.h> header file. */
|
||||||
|
/* #undef HAVE_LINUX_NETLINK_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <memory.h> header file. */
|
||||||
|
#define HAVE_MEMORY_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `pipe2' function. */
|
||||||
|
#define HAVE_PIPE2 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <poll.h> header file. */
|
||||||
|
#define HAVE_POLL_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <stdint.h> header file. */
|
||||||
|
#define HAVE_STDINT_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||||
|
#define HAVE_STDLIB_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <strings.h> header file. */
|
||||||
|
#define HAVE_STRINGS_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <string.h> header file. */
|
||||||
|
#define HAVE_STRING_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if the system has the type `struct timespec'. */
|
||||||
|
#define HAVE_STRUCT_TIMESPEC 1
|
||||||
|
|
||||||
|
/* syslog() function available */
|
||||||
|
/* #undef HAVE_SYSLOG_FUNC */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <syslog.h> header file. */
|
||||||
|
/* #undef HAVE_SYSLOG_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/socket.h> header file. */
|
||||||
|
/* #undef HAVE_SYS_SOCKET_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||||
|
#define HAVE_SYS_STAT_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/time.h> header file. */
|
||||||
|
#define HAVE_SYS_TIME_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||||
|
#define HAVE_SYS_TYPES_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <unistd.h> header file. */
|
||||||
|
#define HAVE_UNISTD_H 1
|
||||||
|
|
||||||
|
/* Define to the sub-directory where libtool stores uninstalled libraries. */
|
||||||
|
#define LT_OBJDIR ".libs/"
|
||||||
|
|
||||||
|
/* Darwin backend */
|
||||||
|
/* #undef OS_DARWIN */
|
||||||
|
|
||||||
|
/* Haiku backend */
|
||||||
|
/* #undef OS_HAIKU */
|
||||||
|
|
||||||
|
/* Linux backend */
|
||||||
|
#define OS_LINUX 1
|
||||||
|
|
||||||
|
/* NetBSD backend */
|
||||||
|
/* #undef OS_NETBSD */
|
||||||
|
|
||||||
|
/* OpenBSD backend */
|
||||||
|
/* #undef OS_OPENBSD */
|
||||||
|
|
||||||
|
/* SunOS backend */
|
||||||
|
/* #undef OS_SUNOS */
|
||||||
|
|
||||||
|
/* Windows backend */
|
||||||
|
/* #undef OS_WINDOWS */
|
||||||
|
|
||||||
|
/* Name of package */
|
||||||
|
#define PACKAGE "libusb"
|
||||||
|
|
||||||
|
/* Define to the address where bug reports for this package should be sent. */
|
||||||
|
#define PACKAGE_BUGREPORT "libusb-devel@lists.sourceforge.net"
|
||||||
|
|
||||||
|
/* Define to the full name of this package. */
|
||||||
|
#define PACKAGE_NAME "libusb"
|
||||||
|
|
||||||
|
/* Define to the full name and version of this package. */
|
||||||
|
#define PACKAGE_STRING "libusb 1.0.22"
|
||||||
|
|
||||||
|
/* Define to the one symbol short name of this package. */
|
||||||
|
#define PACKAGE_TARNAME "libusb"
|
||||||
|
|
||||||
|
/* Define to the home page for this package. */
|
||||||
|
#define PACKAGE_URL "http://libusb.info"
|
||||||
|
|
||||||
|
/* Define to the version of this package. */
|
||||||
|
#define PACKAGE_VERSION "1.0.22"
|
||||||
|
|
||||||
|
/* type of second poll() argument */
|
||||||
|
#define POLL_NFDS_TYPE nfds_t
|
||||||
|
|
||||||
|
/* Define to 1 if you have the ANSI C header files. */
|
||||||
|
#define STDC_HEADERS 1
|
||||||
|
|
||||||
|
/* Use POSIX Threads */
|
||||||
|
#define THREADS_POSIX 1
|
||||||
|
|
||||||
|
/* timerfd headers available */
|
||||||
|
#define USBI_TIMERFD_AVAILABLE 1
|
||||||
|
|
||||||
|
/* Enable output to system log */
|
||||||
|
/* #undef USE_SYSTEM_LOGGING_FACILITY */
|
||||||
|
|
||||||
|
/* Use udev for device enumeration/hotplug */
|
||||||
|
#define USE_UDEV 1
|
||||||
|
|
||||||
|
/* Version number of package */
|
||||||
|
#define VERSION "1.0.22"
|
||||||
|
|
||||||
|
/* Oldest Windows version supported */
|
||||||
|
/* #undef WINVER */
|
||||||
|
|
||||||
|
/* Oldest Windows version supported */
|
||||||
|
/* #undef _WIN32_WINNT */
|
||||||
|
|
||||||
|
/* Define to `__inline__' or `__inline' if that's what the C compiler
|
||||||
|
calls it, or to nothing if 'inline' is not supported under any name. */
|
||||||
|
#ifndef __cplusplus
|
||||||
|
/* #undef inline */
|
||||||
|
#endif
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 0034b2afdcdb1614e78edaa2a9e22d5936aeae5d
|
|
@ -1,7 +1,5 @@
|
||||||
if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
|
if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
|
||||||
if (NOT TARGET project_libuvc)
|
if (NOT TARGET project_libuvc)
|
||||||
find_package(LibUSB REQUIRED)
|
|
||||||
|
|
||||||
set(CMAKE_BUILD_TARGET Static)
|
set(CMAKE_BUILD_TARGET Static)
|
||||||
|
|
||||||
include(ExternalProject)
|
include(ExternalProject)
|
||||||
|
|
|
@ -37,6 +37,54 @@ extern "C" {
|
||||||
*/
|
*/
|
||||||
K4A_EXPORT uint32_t k4a_device_get_installed_count(void);
|
K4A_EXPORT uint32_t k4a_device_get_installed_count(void);
|
||||||
|
|
||||||
|
/** Sets and clears the callback function to recieve debug messages from the Azure Kinect device.
|
||||||
|
*
|
||||||
|
* \param message_cb
|
||||||
|
* The callback function to recieve messages from. Set to NULL to unregister the callback function.
|
||||||
|
*
|
||||||
|
* \param message_cb_context
|
||||||
|
* The callback functions context.
|
||||||
|
*
|
||||||
|
* \param min_level
|
||||||
|
* The least critical error the user wants to be notified about.
|
||||||
|
*
|
||||||
|
* \return ::K4A_RESULT_SUCCEEDED if the callback function was set or cleared successfully. ::K4A_RESULT_FAILED if an
|
||||||
|
* error is encountered or the callback function has already been set.
|
||||||
|
*
|
||||||
|
* \remarks
|
||||||
|
* Call this function to set or clear the callback function that is used to deliver debug messages to the caller. This
|
||||||
|
* callback may be called concurrently, it is up to the implementation of the callback function to ensure the
|
||||||
|
* parallelization is handled.
|
||||||
|
*
|
||||||
|
* \remarks
|
||||||
|
* Clearing the callback function will block until all pending calls to the callback function have completed.
|
||||||
|
*
|
||||||
|
* \remarks
|
||||||
|
* To update \p min_level, \p k4a_set_debug_message_handler can be called with the same value \p message_cb and by
|
||||||
|
* specifying a new \p min_level.
|
||||||
|
*
|
||||||
|
* \remarks
|
||||||
|
* Logging provided via this API is independent of the logging controlled by the environmental variable controls \p
|
||||||
|
* K4A_ENABLE_LOG_TO_STDOUT, \p K4A_ENABLE_LOG_TO_A_FILE, and \p K4A_LOG_LEVEL. However there is a slight change in
|
||||||
|
* default behavior when using this function. By default, when \p k4a_set_debug_message_handler() has not been used to
|
||||||
|
* register a message callback, the default for environmental variable controls is to send debug messages as if
|
||||||
|
* K4A_ENABLE_LOG_TO_STDOUT=1 were set. If \p k4a_set_debug_message_handler registers a callback function before
|
||||||
|
* k4a_device_open() is called, then the default for environmental controls is as if K4A_ENABLE_LOG_TO_STDOUT=0 was
|
||||||
|
* specified. Physically specifying the environmental control will override the default.
|
||||||
|
*
|
||||||
|
* \p min_level
|
||||||
|
* \xmlonly
|
||||||
|
* <requirements>
|
||||||
|
* <requirement name="Header">k4a.h (include k4a/k4a.h)</requirement>
|
||||||
|
* <requirement name="Library">k4a.lib</requirement>
|
||||||
|
* <requirement name="DLL">k4a.dll</requirement>
|
||||||
|
* </requirements>
|
||||||
|
* \endxmlonly
|
||||||
|
*/
|
||||||
|
K4A_EXPORT k4a_result_t k4a_set_debug_message_handler(k4a_logging_message_cb_t *message_cb,
|
||||||
|
void *message_cb_context,
|
||||||
|
k4a_log_level_t min_level);
|
||||||
|
|
||||||
/** Open an Azure Kinect device.
|
/** Open an Azure Kinect device.
|
||||||
*
|
*
|
||||||
* \param index
|
* \param index
|
||||||
|
|
|
@ -181,7 +181,8 @@ K4A_DECLARE_HANDLE(k4a_image_t);
|
||||||
K4A_DECLARE_HANDLE(k4a_transformation_t);
|
K4A_DECLARE_HANDLE(k4a_transformation_t);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* environment variables
|
* Environment Variables
|
||||||
|
*
|
||||||
* K4A_ENABLE_LOG_TO_A_FILE =
|
* K4A_ENABLE_LOG_TO_A_FILE =
|
||||||
* 0 - completely disable logging to a file
|
* 0 - completely disable logging to a file
|
||||||
* log\custom.log - log all messages to the path and file specified - must end in '.log' to
|
* log\custom.log - log all messages to the path and file specified - must end in '.log' to
|
||||||
|
@ -199,6 +200,8 @@ K4A_DECLARE_HANDLE(k4a_transformation_t);
|
||||||
* 'i' - log all messages of level 'info' or higher criticality
|
* 'i' - log all messages of level 'info' or higher criticality
|
||||||
* 't' - log all messages of level 'trace' or higher criticality
|
* 't' - log all messages of level 'trace' or higher criticality
|
||||||
* DEFAULT - log all message of level 'error' or higher criticality
|
* DEFAULT - log all message of level 'error' or higher criticality
|
||||||
|
*
|
||||||
|
* See remarks section of \p k4a_set_debug_message_handler
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** Default device index.
|
/** Default device index.
|
||||||
|
@ -223,28 +226,6 @@ K4A_DECLARE_HANDLE(k4a_transformation_t);
|
||||||
*/
|
*/
|
||||||
#define K4A_WAIT_INFINITE (-1)
|
#define K4A_WAIT_INFINITE (-1)
|
||||||
|
|
||||||
/** Callback function for a memory object being destroyed.
|
|
||||||
*
|
|
||||||
* \param buffer
|
|
||||||
* The buffer pointer that was supplied by the caller as \p buffer_release_cb to \ref k4a_image_create_from_buffer().
|
|
||||||
*
|
|
||||||
* \param context
|
|
||||||
* The context for the memory object that needs to be destroyed that was supplied by the caller as \p
|
|
||||||
* buffer_release_cb_context to \ref k4a_image_create_from_buffer().
|
|
||||||
*
|
|
||||||
* \remarks
|
|
||||||
* When all references for the memory object are released, this callback will be invoked as the final destroy for the
|
|
||||||
* given memory.
|
|
||||||
*
|
|
||||||
* \xmlonly
|
|
||||||
* <requirements>
|
|
||||||
* <requirement name="Header">k4atypes.h (include k4a/k4a.h)</requirement>
|
|
||||||
* </requirements>
|
|
||||||
* \endxmlonly
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
typedef void(k4a_memory_destroy_cb_t)(void *buffer, void *context);
|
|
||||||
|
|
||||||
/** Result code returned by Azure Kinect APIs.
|
/** Result code returned by Azure Kinect APIs.
|
||||||
*
|
*
|
||||||
* \xmlonly
|
* \xmlonly
|
||||||
|
@ -309,6 +290,24 @@ typedef enum
|
||||||
*/
|
*/
|
||||||
#define K4A_FAILED(_result_) (!K4A_SUCCEEDED(_result_))
|
#define K4A_FAILED(_result_) (!K4A_SUCCEEDED(_result_))
|
||||||
|
|
||||||
|
/** Verbosity levels of debug messaging
|
||||||
|
*
|
||||||
|
* \xmlonly
|
||||||
|
* <requirements>
|
||||||
|
* <requirement name="Header">k4atypes.h (include k4a/k4a.h)</requirement>
|
||||||
|
* </requirements>
|
||||||
|
* \endxmlonly
|
||||||
|
*/
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
K4A_LOG_LEVEL_CRITICAL = 0, /**< Most severe level of debug messaging. */
|
||||||
|
K4A_LOG_LEVEL_ERROR, /**< 2nd most severe level of debug messaging. */
|
||||||
|
K4A_LOG_LEVEL_WARNING, /**< 3nd most severe level of debug messaging. */
|
||||||
|
K4A_LOG_LEVEL_INFO, /**< 2nd least severe level of debug messaging. */
|
||||||
|
K4A_LOG_LEVEL_TRACE, /**< Least severe level of debug messaging. */
|
||||||
|
K4A_LOG_LEVEL_OFF, /**< No logging is performed */
|
||||||
|
} k4a_log_level_t;
|
||||||
|
|
||||||
/** Depth sensor capture modes.
|
/** Depth sensor capture modes.
|
||||||
*
|
*
|
||||||
* \remarks
|
* \remarks
|
||||||
|
@ -1026,6 +1025,68 @@ typedef struct _k4a_calibration_t
|
||||||
k4a_color_resolution_t color_resolution; /**< Color camera resolution for which calibration was obtained. */
|
k4a_color_resolution_t color_resolution; /**< Color camera resolution for which calibration was obtained. */
|
||||||
} k4a_calibration_t;
|
} k4a_calibration_t;
|
||||||
|
|
||||||
|
/** Callback function for a memory object being destroyed.
|
||||||
|
*
|
||||||
|
* \param buffer
|
||||||
|
* The buffer pointer that was supplied by the caller as \p buffer_release_cb to \ref k4a_image_create_from_buffer().
|
||||||
|
*
|
||||||
|
* \param context
|
||||||
|
* The context for the memory object that needs to be destroyed that was supplied by the caller as \p
|
||||||
|
* buffer_release_cb_context to \ref k4a_image_create_from_buffer().
|
||||||
|
*
|
||||||
|
* \remarks
|
||||||
|
* When all references for the memory object are released, this callback will be invoked as the final destroy for the
|
||||||
|
* given memory.
|
||||||
|
*
|
||||||
|
* \xmlonly
|
||||||
|
* <requirements>
|
||||||
|
* <requirement name="Header">k4atypes.h (include k4a/k4a.h)</requirement>
|
||||||
|
* </requirements>
|
||||||
|
* \endxmlonly
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef void(k4a_memory_destroy_cb_t)(void *buffer, void *context);
|
||||||
|
|
||||||
|
/** Callback function for debug messages being generated by the Azure Kinect SDK.
|
||||||
|
*
|
||||||
|
* \param context
|
||||||
|
* The context of the callback function. This is the context that was supplied by the caller to \p
|
||||||
|
* k4a_set_debug_message_handler.
|
||||||
|
*
|
||||||
|
* \param level
|
||||||
|
* The level of the message that has been created.
|
||||||
|
*
|
||||||
|
* \param file
|
||||||
|
* The file name of the source file that generated the message.
|
||||||
|
*
|
||||||
|
* \param line
|
||||||
|
* The line number of the source file that generated the message.
|
||||||
|
*
|
||||||
|
* \param message
|
||||||
|
* The messaged generated by the Azure Kinect SDK.
|
||||||
|
*
|
||||||
|
* \remarks
|
||||||
|
* The callback is called asynchronously when the Azure Kinext SDK generates a message at a \p level that is equal to
|
||||||
|
* or more critical than the level specified when calling \ref k4a_set_debug_message_handler() to register the callback.
|
||||||
|
*
|
||||||
|
* \remarks
|
||||||
|
* This callback can occur from any thread and blocks the calling thread. The k4a_logging_message_cb_t function user
|
||||||
|
* must protect it's logging resources from concurrent calls. All care should be made to minimize the amount of time
|
||||||
|
* locks are held.
|
||||||
|
*
|
||||||
|
* \xmlonly
|
||||||
|
* <requirements>
|
||||||
|
* <requirement name="Header">k4atypes.h (include k4a/k4a.h)</requirement>
|
||||||
|
* </requirements>
|
||||||
|
* \endxmlonly
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef void(k4a_logging_message_cb_t)(void *context,
|
||||||
|
k4a_log_level_t level,
|
||||||
|
const char *file,
|
||||||
|
const int line,
|
||||||
|
const char *message);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
#define ALLOCATOR_H
|
#define ALLOCATOR_H
|
||||||
|
|
||||||
#include <k4a/k4atypes.h>
|
#include <k4a/k4atypes.h>
|
||||||
#include <k4ainternal/logging.h>
|
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
|
|
||||||
#include <k4a/k4atypes.h>
|
#include <k4a/k4atypes.h>
|
||||||
#include <k4ainternal/handle.h>
|
#include <k4ainternal/handle.h>
|
||||||
#include <k4ainternal/logging.h>
|
|
||||||
#include <k4ainternal/usbcommand.h>
|
#include <k4ainternal/usbcommand.h>
|
||||||
#include <k4ainternal/allocator.h>
|
#include <k4ainternal/allocator.h>
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
|
|
||||||
#include <k4a/k4atypes.h>
|
#include <k4a/k4atypes.h>
|
||||||
#include <k4ainternal/handle.h>
|
#include <k4ainternal/handle.h>
|
||||||
#include <k4ainternal/logging.h>
|
|
||||||
#include <k4ainternal/depth_mcu.h>
|
#include <k4ainternal/depth_mcu.h>
|
||||||
#include <k4ainternal/calibration.h>
|
#include <k4ainternal/calibration.h>
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
#include <k4a/k4atypes.h>
|
#include <k4a/k4atypes.h>
|
||||||
#include <k4ainternal/common.h>
|
#include <k4ainternal/common.h>
|
||||||
#include <k4ainternal/handle.h>
|
#include <k4ainternal/handle.h>
|
||||||
#include <k4ainternal/logging.h>
|
|
||||||
#include <k4ainternal/usbcommand.h>
|
#include <k4ainternal/usbcommand.h>
|
||||||
#include <k4ainternal/allocator.h>
|
#include <k4ainternal/allocator.h>
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
|
|
||||||
#include <k4a/k4atypes.h>
|
#include <k4a/k4atypes.h>
|
||||||
#include <k4ainternal/handle.h>
|
#include <k4ainternal/handle.h>
|
||||||
#include <k4ainternal/logging.h>
|
|
||||||
#include <k4ainternal/allocator.h>
|
#include <k4ainternal/allocator.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
|
|
||||||
#include <k4a/k4atypes.h>
|
#include <k4a/k4atypes.h>
|
||||||
#include <k4ainternal/handle.h>
|
#include <k4ainternal/handle.h>
|
||||||
#include <k4ainternal/logging.h>
|
|
||||||
#include <k4ainternal/color_mcu.h>
|
#include <k4ainternal/color_mcu.h>
|
||||||
#include <k4ainternal/calibration.h>
|
#include <k4ainternal/calibration.h>
|
||||||
#include <azure_c_shared_utility/tickcounter.h>
|
#include <azure_c_shared_utility/tickcounter.h>
|
||||||
|
|
|
@ -33,17 +33,13 @@ extern "C" {
|
||||||
*/
|
*/
|
||||||
K4A_DECLARE_HANDLE(logger_t);
|
K4A_DECLARE_HANDLE(logger_t);
|
||||||
|
|
||||||
/** Logger zones
|
|
||||||
*/
|
|
||||||
#define LOGGER_K4A "K4A"
|
|
||||||
|
|
||||||
/** Default logging settings
|
/** Default logging settings
|
||||||
*/
|
*/
|
||||||
#define K4A_ENABLE_LOG_TO_A_FILE "K4A_ENABLE_LOG_TO_A_FILE"
|
#define K4A_ENABLE_LOG_TO_A_FILE "K4A_ENABLE_LOG_TO_A_FILE"
|
||||||
#define K4A_ENABLE_LOG_TO_STDOUT "K4A_ENABLE_LOG_TO_STDOUT"
|
#define K4A_ENABLE_LOG_TO_STDOUT "K4A_ENABLE_LOG_TO_STDOUT"
|
||||||
#define K4A_LOG_LEVEL "K4A_LOG_LEVEL"
|
#define K4A_LOG_LEVEL "K4A_LOG_LEVEL"
|
||||||
#define K4A_LOG_FILE_NAME "k4a.log"
|
#define K4A_LOG_FILE_NAME "k4a.log"
|
||||||
#define K4A_LOG_FILE_10MB_MAX_SIZE (1048576 * 10)
|
#define K4A_LOG_FILE_50MB_MAX_SIZE (1048576 * 50)
|
||||||
|
|
||||||
/** Logger configuration - allows logger to be used in seperate DLL's and
|
/** Logger configuration - allows logger to be used in seperate DLL's and
|
||||||
provide different ENV vars for processes that need to load both instances.
|
provide different ENV vars for processes that need to load both instances.
|
||||||
|
@ -67,7 +63,7 @@ static inline void logger_config_init_default(logger_config_t *config)
|
||||||
config->env_var_log_to_stdout = K4A_ENABLE_LOG_TO_STDOUT;
|
config->env_var_log_to_stdout = K4A_ENABLE_LOG_TO_STDOUT;
|
||||||
config->env_var_log_level = K4A_LOG_LEVEL;
|
config->env_var_log_level = K4A_LOG_LEVEL;
|
||||||
config->log_file = NULL;
|
config->log_file = NULL;
|
||||||
config->max_log_size = K4A_LOG_FILE_10MB_MAX_SIZE;
|
config->max_log_size = K4A_LOG_FILE_50MB_MAX_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Open a handle to the logger device.
|
/** Open a handle to the logger device.
|
||||||
|
@ -95,35 +91,32 @@ void logger_destroy(logger_t logger_handle);
|
||||||
*/
|
*/
|
||||||
bool logger_is_file_based(void);
|
bool logger_is_file_based(void);
|
||||||
|
|
||||||
/** Logs a message to the configured logger at an 'critical' level
|
/** Registers a callback function to deliver messages to.
|
||||||
* */
|
*
|
||||||
void logger_critical(const char *zone, const char *format, ...);
|
* \param message_cb [IN]
|
||||||
|
* callback function for delivering message to. Set to NULL to unregister a callback function.
|
||||||
|
*
|
||||||
|
* \param message_cb_context [IN]
|
||||||
|
* The callback functions context.
|
||||||
|
*
|
||||||
|
* \param min_message_level [IN]
|
||||||
|
* The least critical error the user wants to be notified about.
|
||||||
|
*
|
||||||
|
* \remarks
|
||||||
|
* See \ref k4a_set_debug_message_handler for more detailed documentation.
|
||||||
|
*/
|
||||||
|
k4a_result_t logger_register_message_callback(k4a_logging_message_cb_t *message_cb,
|
||||||
|
void *message_cb_context,
|
||||||
|
k4a_log_level_t min_message_level);
|
||||||
|
|
||||||
/** Logs a message to the configured logger at an 'error' level
|
void logger_log(k4a_log_level_t level, const char *file, const int line, const char *format, ...);
|
||||||
* */
|
|
||||||
void logger_error(const char *zone, const char *format, ...);
|
|
||||||
|
|
||||||
/** Logs a message to the configured logger at an 'warning' level
|
|
||||||
* */
|
|
||||||
void logger_warn(const char *zone, const char *format, ...);
|
|
||||||
|
|
||||||
/** Logs a message to the configured logger at an 'informational' level
|
|
||||||
* */
|
|
||||||
void logger_info(const char *zone, const char *format, ...);
|
|
||||||
|
|
||||||
/** Logs a message to the configured logger at an 'trace' level
|
|
||||||
* */
|
|
||||||
void logger_trace(const char *zone, const char *format, ...);
|
|
||||||
|
|
||||||
FORCEINLINE k4a_result_t
|
FORCEINLINE k4a_result_t
|
||||||
TraceError(k4a_result_t result, const char *szCall, const char *szFile, int line, const char *szFunction)
|
TraceError(k4a_result_t result, const char *szCall, const char *szFile, int line, const char *szFunction)
|
||||||
{
|
{
|
||||||
if (K4A_FAILED(result))
|
if (K4A_FAILED(result))
|
||||||
{
|
{
|
||||||
// Example print:
|
logger_log(K4A_LOG_LEVEL_ERROR, szFile, line, "%s returned failure in %s()", szCall, szFunction);
|
||||||
// depth.cpp (86): allocator_create(&depth->allocator) returned failure in depth_create
|
|
||||||
|
|
||||||
logger_error(LOGGER_K4A, "%s (%d): %s returned failure in %s()", szFile, line, szCall, szFunction);
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -132,10 +125,7 @@ TraceBufferError(k4a_buffer_result_t result, const char *szCall, const char *szF
|
||||||
{
|
{
|
||||||
if (result == K4A_BUFFER_RESULT_FAILED)
|
if (result == K4A_BUFFER_RESULT_FAILED)
|
||||||
{
|
{
|
||||||
// Example print:
|
logger_log(K4A_LOG_LEVEL_ERROR, szFile, line, "%s returned failure in %s()", szCall, szFunction);
|
||||||
// depth.cpp (86): allocator_create(&depth->allocator) returned failure in depth_create
|
|
||||||
|
|
||||||
logger_error(LOGGER_K4A, "%s (%d): %s returned failure in %s()", szFile, line, szCall, szFunction);
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -144,10 +134,7 @@ TraceWaitError(k4a_wait_result_t result, const char *szCall, const char *szFile,
|
||||||
{
|
{
|
||||||
if (result == K4A_WAIT_RESULT_FAILED)
|
if (result == K4A_WAIT_RESULT_FAILED)
|
||||||
{
|
{
|
||||||
// Example print:
|
logger_log(K4A_LOG_LEVEL_ERROR, szFile, line, "%s returned failure in %s()", szCall, szFunction);
|
||||||
// depth.cpp (86): allocator_create(&depth->allocator) returned failure in depth_create
|
|
||||||
|
|
||||||
logger_error(LOGGER_K4A, "%s (%d): %s returned failure in %s()", szFile, line, szCall, szFunction);
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -156,14 +143,11 @@ FORCEINLINE k4a_result_t TraceReturn(k4a_result_t result, const char *szFile, in
|
||||||
{
|
{
|
||||||
if (K4A_FAILED(result))
|
if (K4A_FAILED(result))
|
||||||
{
|
{
|
||||||
// Example print:
|
logger_log(K4A_LOG_LEVEL_ERROR, szFile, line, "%s() returned failure.", szFunction);
|
||||||
// depth.cpp (86): depth_create returned failure.
|
|
||||||
|
|
||||||
logger_error(LOGGER_K4A, "%s (%d): %s() returned failure.", szFile, line, szFunction);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
logger_trace(LOGGER_K4A, "%s (%d): %s() returned success.", szFile, line, szFunction);
|
logger_log(K4A_LOG_LEVEL_TRACE, szFile, line, "%s() returned success.", szFunction);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -172,9 +156,7 @@ FORCEINLINE void TraceArg(int result, const char *szFile, int line, const char *
|
||||||
{
|
{
|
||||||
if (!result)
|
if (!result)
|
||||||
{
|
{
|
||||||
// Example print:
|
logger_log(K4A_LOG_LEVEL_ERROR, szFile, line, "Invalid argument to %s(). %s", szFunction, szExpression);
|
||||||
// depth.cpp (86): Invalid argument to depth_create. depthmcu == NULL.
|
|
||||||
logger_error(LOGGER_K4A, "%s (%d): Invalid argument to %s(). %s", szFile, line, szFunction, szExpression);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,17 +170,14 @@ FORCEINLINE void TraceInvalidHandle(int result,
|
||||||
{
|
{
|
||||||
if (!result)
|
if (!result)
|
||||||
{
|
{
|
||||||
// Example print:
|
logger_log(K4A_LOG_LEVEL_ERROR,
|
||||||
// depth.cpp (86): Invalid argument to depth_create. depth_handle (00000000) is not a valid handle of type
|
szFile,
|
||||||
// depth_t
|
line,
|
||||||
logger_error(LOGGER_K4A,
|
"Invalid argument to %s(). %s (%p) is not a valid handle of type %s",
|
||||||
"%s (%d): Invalid argument to %s(). %s (%p) is not a valid handle of type %s",
|
szFunction,
|
||||||
szFile,
|
szExpression,
|
||||||
line,
|
pHandleValue,
|
||||||
szFunction,
|
szHandleType);
|
||||||
szExpression,
|
|
||||||
pHandleValue,
|
|
||||||
szHandleType);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -278,15 +257,17 @@ FORCEINLINE void TraceInvalidHandle(int result,
|
||||||
|
|
||||||
// Logs a message
|
// Logs a message
|
||||||
#define LOG_TRACE(message, ...) \
|
#define LOG_TRACE(message, ...) \
|
||||||
logger_trace(LOGGER_K4A, "%s (%d): %s(). " message, __FILE__, __LINE__, __func__, __VA_ARGS__)
|
logger_log(K4A_LOG_LEVEL_TRACE, __FILE__, __LINE__, "%s(). " message, __func__, __VA_ARGS__)
|
||||||
#define LOG_INFO(message, ...) \
|
#define LOG_INFO(message, ...) \
|
||||||
logger_info(LOGGER_K4A, "%s (%d): %s(). " message, __FILE__, __LINE__, __func__, __VA_ARGS__)
|
logger_log(K4A_LOG_LEVEL_INFO, __FILE__, __LINE__, "%s(). " message, __func__, __VA_ARGS__)
|
||||||
#define LOG_WARNING(message, ...) \
|
#define LOG_WARNING(message, ...) \
|
||||||
logger_warn(LOGGER_K4A, "%s (%d): %s(). " message, __FILE__, __LINE__, __func__, __VA_ARGS__)
|
logger_log(K4A_LOG_LEVEL_WARNING, __FILE__, __LINE__, "%s(). " message, __func__, __VA_ARGS__)
|
||||||
#define LOG_ERROR(message, ...) \
|
#define LOG_ERROR(message, ...) \
|
||||||
logger_error(LOGGER_K4A, "%s (%d): %s(). " message, __FILE__, __LINE__, __func__, __VA_ARGS__)
|
logger_log(K4A_LOG_LEVEL_ERROR, __FILE__, __LINE__, "%s(). " message, __func__, __VA_ARGS__)
|
||||||
|
#define LOG_CRITICAL(message, ...) \
|
||||||
|
logger_log(K4A_LOG_LEVEL_CRITICAL, __FILE__, __LINE__, "%s(). " message, __func__, __VA_ARGS__)
|
||||||
#define LOG_HANDLE(message, ...) \
|
#define LOG_HANDLE(message, ...) \
|
||||||
logger_info(LOGGER_K4A, "%s (%d): %s(). " message, __FILE__, __LINE__, __func__, __VA_ARGS__)
|
logger_log(K4A_LOG_LEVEL_TRACE, __FILE__, __LINE__, "%s(). " message, __func__, __VA_ARGS__)
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,10 @@
|
||||||
|
|
||||||
namespace k4arecord
|
namespace k4arecord
|
||||||
{
|
{
|
||||||
|
// The depth mode string for legacy recordings
|
||||||
|
static const std::pair<k4a_depth_mode_t, std::string> legacy_depth_modes[] =
|
||||||
|
{ { K4A_DEPTH_MODE_NFOV_2X2BINNED, "NFOV_2x2BINNED" }, { K4A_DEPTH_MODE_WFOV_2X2BINNED, "WFOV_2x2BINNED" } };
|
||||||
|
|
||||||
typedef struct _cluster_info_t
|
typedef struct _cluster_info_t
|
||||||
{
|
{
|
||||||
// The cluster size will be 0 until the actual cluster has been read from disk.
|
// The cluster size will be 0 until the actual cluster has been read from disk.
|
||||||
|
@ -84,6 +88,7 @@ typedef struct _k4a_playback_context_t
|
||||||
|
|
||||||
uint64_t timecode_scale;
|
uint64_t timecode_scale;
|
||||||
k4a_record_configuration_t record_config;
|
k4a_record_configuration_t record_config;
|
||||||
|
k4a_image_format_t color_format_conversion;
|
||||||
|
|
||||||
std::unique_ptr<libebml::EbmlStream> stream;
|
std::unique_ptr<libebml::EbmlStream> stream;
|
||||||
std::unique_ptr<libmatroska::KaxSegment> segment;
|
std::unique_ptr<libmatroska::KaxSegment> segment;
|
||||||
|
@ -137,8 +142,7 @@ k4a_result_t parse_recording_config(k4a_playback_context_t *context);
|
||||||
k4a_result_t read_bitmap_info_header(track_reader_t *track);
|
k4a_result_t read_bitmap_info_header(track_reader_t *track);
|
||||||
void reset_seek_pointers(k4a_playback_context_t *context, uint64_t seek_timestamp_ns);
|
void reset_seek_pointers(k4a_playback_context_t *context, uint64_t seek_timestamp_ns);
|
||||||
|
|
||||||
libmatroska::KaxTrackEntry *get_track_by_name(k4a_playback_context_t *context, const char *name);
|
libmatroska::KaxTrackEntry *find_track(k4a_playback_context_t *context, const char *name, const char *tag_name);
|
||||||
libmatroska::KaxTrackEntry *get_track_by_tag(k4a_playback_context_t *context, const char *tag_name);
|
|
||||||
libmatroska::KaxTag *get_tag(k4a_playback_context_t *context, const char *name);
|
libmatroska::KaxTag *get_tag(k4a_playback_context_t *context, const char *name);
|
||||||
std::string get_tag_string(libmatroska::KaxTag *tag);
|
std::string get_tag_string(libmatroska::KaxTag *tag);
|
||||||
libmatroska::KaxAttached *get_attachment_by_name(k4a_playback_context_t *context, const char *file_name);
|
libmatroska::KaxAttached *get_attachment_by_name(k4a_playback_context_t *context, const char *file_name);
|
||||||
|
@ -150,6 +154,8 @@ void populate_cluster_info(k4a_playback_context_t *context,
|
||||||
cluster_info_t *cluster_info);
|
cluster_info_t *cluster_info);
|
||||||
cluster_info_t *find_cluster(k4a_playback_context_t *context, uint64_t timestamp_ns);
|
cluster_info_t *find_cluster(k4a_playback_context_t *context, uint64_t timestamp_ns);
|
||||||
cluster_info_t *next_cluster(k4a_playback_context_t *context, cluster_info_t *current, bool next);
|
cluster_info_t *next_cluster(k4a_playback_context_t *context, cluster_info_t *current, bool next);
|
||||||
|
std::shared_ptr<libmatroska::KaxCluster> load_cluster_internal(k4a_playback_context_t *context,
|
||||||
|
cluster_info_t *cluster_info);
|
||||||
std::shared_ptr<loaded_cluster_t> load_cluster(k4a_playback_context_t *context, cluster_info_t *cluster_info);
|
std::shared_ptr<loaded_cluster_t> load_cluster(k4a_playback_context_t *context, cluster_info_t *cluster_info);
|
||||||
std::shared_ptr<loaded_cluster_t> load_next_cluster(k4a_playback_context_t *context,
|
std::shared_ptr<loaded_cluster_t> load_next_cluster(k4a_playback_context_t *context,
|
||||||
loaded_cluster_t *current_cluster,
|
loaded_cluster_t *current_cluster,
|
||||||
|
@ -160,6 +166,10 @@ std::shared_ptr<block_info_t> find_block(k4a_playback_context_t *context,
|
||||||
uint64_t timestamp_ns);
|
uint64_t timestamp_ns);
|
||||||
std::shared_ptr<block_info_t> next_block(k4a_playback_context_t *context, block_info_t *current, bool next);
|
std::shared_ptr<block_info_t> next_block(k4a_playback_context_t *context, block_info_t *current, bool next);
|
||||||
|
|
||||||
|
k4a_result_t convert_block_to_image(k4a_playback_context_t *context,
|
||||||
|
block_info_t *in_block,
|
||||||
|
k4a_image_t *image_out,
|
||||||
|
k4a_image_format_t target_format);
|
||||||
k4a_result_t new_capture(k4a_playback_context_t *context, block_info_t *block, k4a_capture_t *capture_handle);
|
k4a_result_t new_capture(k4a_playback_context_t *context, block_info_t *block, k4a_capture_t *capture_handle);
|
||||||
k4a_stream_result_t get_capture(k4a_playback_context_t *context, k4a_capture_t *capture_handle, bool next);
|
k4a_stream_result_t get_capture(k4a_playback_context_t *context, k4a_capture_t *capture_handle, bool next);
|
||||||
k4a_stream_result_t get_imu_sample(k4a_playback_context_t *context, k4a_imu_sample_t *imu_sample, bool next);
|
k4a_stream_result_t get_imu_sample(k4a_playback_context_t *context, k4a_imu_sample_t *imu_sample, bool next);
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
#define QUEUE_H
|
#define QUEUE_H
|
||||||
|
|
||||||
#include <k4a/k4atypes.h>
|
#include <k4a/k4atypes.h>
|
||||||
#include <k4ainternal/logging.h>
|
|
||||||
#include <k4ainternal/capture.h>
|
#include <k4ainternal/capture.h>
|
||||||
#include <k4ainternal/common.h>
|
#include <k4ainternal/common.h>
|
||||||
|
|
||||||
|
|
|
@ -85,6 +85,14 @@ k4a_transformation_t transformation_create(const k4a_calibration_t *calibration,
|
||||||
|
|
||||||
void transformation_destroy(k4a_transformation_t transformation_handle);
|
void transformation_destroy(k4a_transformation_t transformation_handle);
|
||||||
|
|
||||||
|
k4a_buffer_result_t transformation_depth_image_to_color_camera_validate_parameters(
|
||||||
|
const k4a_calibration_t *calibration,
|
||||||
|
const k4a_transformation_xy_tables_t *xy_tables_depth_camera,
|
||||||
|
const uint8_t *depth_image_data,
|
||||||
|
const k4a_transformation_image_descriptor_t *depth_image_descriptor,
|
||||||
|
uint8_t *transformed_depth_image_data,
|
||||||
|
k4a_transformation_image_descriptor_t *transformed_depth_image_descriptor);
|
||||||
|
|
||||||
k4a_buffer_result_t transformation_depth_image_to_color_camera_internal(
|
k4a_buffer_result_t transformation_depth_image_to_color_camera_internal(
|
||||||
const k4a_calibration_t *calibration,
|
const k4a_calibration_t *calibration,
|
||||||
const k4a_transformation_xy_tables_t *xy_tables_depth_camera,
|
const k4a_transformation_xy_tables_t *xy_tables_depth_camera,
|
||||||
|
@ -100,6 +108,16 @@ transformation_depth_image_to_color_camera(k4a_transformation_t transformation_h
|
||||||
uint8_t *transformed_depth_image_data,
|
uint8_t *transformed_depth_image_data,
|
||||||
k4a_transformation_image_descriptor_t *transformed_depth_image_descriptor);
|
k4a_transformation_image_descriptor_t *transformed_depth_image_descriptor);
|
||||||
|
|
||||||
|
k4a_buffer_result_t transformation_color_image_to_depth_camera_validate_parameters(
|
||||||
|
const k4a_calibration_t *calibration,
|
||||||
|
const k4a_transformation_xy_tables_t *xy_tables_depth_camera,
|
||||||
|
const uint8_t *depth_image_data,
|
||||||
|
const k4a_transformation_image_descriptor_t *depth_image_descriptor,
|
||||||
|
const uint8_t *color_image_data,
|
||||||
|
const k4a_transformation_image_descriptor_t *color_image_descriptor,
|
||||||
|
uint8_t *transformed_color_image_data,
|
||||||
|
k4a_transformation_image_descriptor_t *transformed_color_image_descriptor);
|
||||||
|
|
||||||
k4a_buffer_result_t transformation_color_image_to_depth_camera_internal(
|
k4a_buffer_result_t transformation_color_image_to_depth_camera_internal(
|
||||||
const k4a_calibration_t *calibration,
|
const k4a_calibration_t *calibration,
|
||||||
const k4a_transformation_xy_tables_t *xy_tables_depth_camera,
|
const k4a_transformation_xy_tables_t *xy_tables_depth_camera,
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
//************************ Includes *****************************
|
//************************ Includes *****************************
|
||||||
#include <k4a/k4atypes.h>
|
#include <k4a/k4atypes.h>
|
||||||
#include <k4ainternal/image.h>
|
#include <k4ainternal/image.h>
|
||||||
#include <k4ainternal/logging.h>
|
|
||||||
#include <k4ainternal/common.h>
|
#include <k4ainternal/common.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
@ -33,7 +32,7 @@ typedef enum
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
CMD_STATUS_PASS = 0,
|
CMD_STATUS_PASS = 0,
|
||||||
} usb_cmd_responces_t;
|
} usb_cmd_responses_t;
|
||||||
|
|
||||||
K4A_DECLARE_HANDLE(usbcmd_t);
|
K4A_DECLARE_HANDLE(usbcmd_t);
|
||||||
|
|
||||||
|
|
|
@ -168,6 +168,40 @@ K4ARECORD_EXPORT k4a_buffer_result_t k4a_playback_get_tag(k4a_playback_t playbac
|
||||||
char *value,
|
char *value,
|
||||||
size_t *value_size);
|
size_t *value_size);
|
||||||
|
|
||||||
|
/** Set the image format that color captures will be converted to. By default the conversion format will be the same as
|
||||||
|
* the image format stored in the recording file, and no conversion will occur.
|
||||||
|
*
|
||||||
|
* \param playback_handle
|
||||||
|
* Handle obtained by k4a_playback_open().
|
||||||
|
*
|
||||||
|
* \param target_format
|
||||||
|
* The target image format to be returned in captures.
|
||||||
|
*
|
||||||
|
* \returns
|
||||||
|
* ::K4A_RESULT_SUCCEEDED if the format conversion is supported. ::K4A_RESULT_FAILED otherwise.
|
||||||
|
*
|
||||||
|
* \remarks
|
||||||
|
* After the color conversion format is set, all \ref k4a_capture_t objects returned from the playback handle will have
|
||||||
|
* their color images converted to the \p target_format.
|
||||||
|
*
|
||||||
|
* \remarks
|
||||||
|
* Color format conversion occurs in the user-thread, so setting \p target_format to anything other than the format
|
||||||
|
* stored in the file may significantly increase the latency of \p k4a_playback_get_next_capture() and \p
|
||||||
|
* k4a_playback_get_previous_capture().
|
||||||
|
*
|
||||||
|
* \relates k4a_playback_t
|
||||||
|
*
|
||||||
|
* \xmlonly
|
||||||
|
* <requirements>
|
||||||
|
* <requirement name="Header">playback.h (include k4arecord/playback.h)</requirement>
|
||||||
|
* <requirement name="Library">k4arecord.lib</requirement>
|
||||||
|
* <requirement name="DLL">k4arecord.dll</requirement>
|
||||||
|
* </requirements>
|
||||||
|
* \endxmlonly
|
||||||
|
*/
|
||||||
|
K4ARECORD_EXPORT k4a_result_t k4a_playback_set_color_conversion(k4a_playback_t playback_handle,
|
||||||
|
k4a_image_format_t target_format);
|
||||||
|
|
||||||
/** Read the next capture in the recording sequence.
|
/** Read the next capture in the recording sequence.
|
||||||
*
|
*
|
||||||
* \param playback_handle
|
* \param playback_handle
|
||||||
|
|
|
@ -32,5 +32,6 @@ sudo apt install -y \
|
||||||
libxrandr-dev \
|
libxrandr-dev \
|
||||||
libusb-1.0-0-dev \
|
libusb-1.0-0-dev \
|
||||||
libssl-dev \
|
libssl-dev \
|
||||||
|
libudev-dev \
|
||||||
mesa-common-dev \
|
mesa-common-dev \
|
||||||
uuid-dev
|
uuid-dev
|
||||||
|
|
|
@ -156,14 +156,16 @@ long allocator_test_for_leaks(void)
|
||||||
if (g_allocated_image_count_user || g_allocated_image_count_depth || g_allocated_image_count_color ||
|
if (g_allocated_image_count_user || g_allocated_image_count_depth || g_allocated_image_count_color ||
|
||||||
g_allocated_image_count_imu || g_allocated_image_count_usb_depth || g_allocated_image_count_usb_imu)
|
g_allocated_image_count_imu || g_allocated_image_count_usb_depth || g_allocated_image_count_usb_imu)
|
||||||
{
|
{
|
||||||
logger_critical(LOGGER_K4A,
|
logger_log(K4A_LOG_LEVEL_CRITICAL,
|
||||||
"Leaked usr:%d, color:%d, depth:%d, imu:%d, usb depth:%d, usb imu%d",
|
__FILE__,
|
||||||
g_allocated_image_count_user,
|
__LINE__,
|
||||||
g_allocated_image_count_color,
|
"Leaked usr:%d, color:%d, depth:%d, imu:%d, usb depth:%d, usb imu%d",
|
||||||
g_allocated_image_count_depth,
|
g_allocated_image_count_user,
|
||||||
g_allocated_image_count_imu,
|
g_allocated_image_count_color,
|
||||||
g_allocated_image_count_usb_depth,
|
g_allocated_image_count_depth,
|
||||||
g_allocated_image_count_usb_imu);
|
g_allocated_image_count_imu,
|
||||||
|
g_allocated_image_count_usb_depth,
|
||||||
|
g_allocated_image_count_usb_imu);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(g_allocated_image_count_user == 0);
|
assert(g_allocated_image_count_user == 0);
|
||||||
|
@ -349,15 +351,6 @@ void capture_set_imu_image(k4a_capture_t capture_handle, k4a_image_t image_handl
|
||||||
capture_set_ir_image(capture_handle, image_handle);
|
capture_set_ir_image(capture_handle, image_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
// On Ubuntu 16.04 this works without warnings, but on Ubuntu 18.04 isnan actually
|
|
||||||
// takes a long double, we get a double-promotion warning here. Unfortunately,
|
|
||||||
// isnan has an implementation-defined argument type, so there's not a specific
|
|
||||||
// type we can cast it to in order to avoid clang's precision warnings, so we
|
|
||||||
// just need to suppress the warning.
|
|
||||||
#ifdef __clang__
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma clang diagnostic ignored "-Wdouble-promotion"
|
|
||||||
#endif
|
|
||||||
void capture_set_temperature_c(k4a_capture_t capture_handle, float temperature_c)
|
void capture_set_temperature_c(k4a_capture_t capture_handle, float temperature_c)
|
||||||
{
|
{
|
||||||
RETURN_VALUE_IF_HANDLE_INVALID(VOID_VALUE, k4a_capture_t, capture_handle);
|
RETURN_VALUE_IF_HANDLE_INVALID(VOID_VALUE, k4a_capture_t, capture_handle);
|
||||||
|
@ -367,10 +360,6 @@ void capture_set_temperature_c(k4a_capture_t capture_handle, float temperature_c
|
||||||
capture->temperature_c = temperature_c;
|
capture->temperature_c = temperature_c;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __clang__
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
#endif
|
|
||||||
|
|
||||||
float capture_get_temperature_c(k4a_capture_t capture_handle)
|
float capture_get_temperature_c(k4a_capture_t capture_handle)
|
||||||
{
|
{
|
||||||
RETURN_VALUE_IF_HANDLE_INVALID(NAN, k4a_capture_t, capture_handle);
|
RETURN_VALUE_IF_HANDLE_INVALID(NAN, k4a_capture_t, capture_handle);
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
#define COLOR_CAMERA_IDENTIFIER L"vid_045e&pid_097d"
|
#define COLOR_CAMERA_IDENTIFIER L"vid_045e&pid_097d"
|
||||||
#define MetadataId_FrameAlignInfo 0x80000001
|
#define MetadataId_FrameAlignInfo 0x80000001
|
||||||
|
|
||||||
#pragma pack(push)
|
#pragma pack(push, 1)
|
||||||
typedef struct tag_CUSTOM_METADATA_FrameAlignInfo
|
typedef struct tag_CUSTOM_METADATA_FrameAlignInfo
|
||||||
{
|
{
|
||||||
KSCAMERA_METADATA_ITEMHEADER Header;
|
KSCAMERA_METADATA_ITEMHEADER Header;
|
||||||
|
@ -22,6 +22,7 @@ typedef struct tag_CUSTOM_METADATA_FrameAlignInfo
|
||||||
ULONG PTSReference;
|
ULONG PTSReference;
|
||||||
ULONGLONG USBSoFSeqNum; // 8 bytes
|
ULONGLONG USBSoFSeqNum; // 8 bytes
|
||||||
ULONGLONG USBSoFPTS; // 8 bytes
|
ULONGLONG USBSoFPTS; // 8 bytes
|
||||||
|
ULONG Synced;
|
||||||
} CUSTOM_METADATA_FrameAlignInfo, *PKSCAMERA_CUSTOM_METADATA_FrameAlignInfo;
|
} CUSTOM_METADATA_FrameAlignInfo, *PKSCAMERA_CUSTOM_METADATA_FrameAlignInfo;
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
|
|
@ -223,53 +223,58 @@ void depth_destroy(depth_t depth_handle)
|
||||||
|
|
||||||
static void log_device_info(depth_context_t *depth)
|
static void log_device_info(depth_context_t *depth)
|
||||||
{
|
{
|
||||||
typedef void(logger_pfn_t)(const char *zone, const char *format, ...);
|
k4a_log_level_t level;
|
||||||
logger_pfn_t *logger = NULL;
|
|
||||||
|
|
||||||
if (logger_is_file_based())
|
if (logger_is_file_based())
|
||||||
{
|
{
|
||||||
// Log device information at a 'critical' level so that even at that no matter what level the logger
|
// Log device information at a 'critical' level so that even at that no matter what level the logger
|
||||||
// is set to, we will always of this critical information about version configuration and hardware
|
// is set to, we will always of this critical information about version configuration and hardware
|
||||||
// revision (hardware revision)
|
// revision (hardware revision)
|
||||||
logger = logger_critical;
|
level = K4A_LOG_LEVEL_CRITICAL;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Log device information to stdout at an 'info' level so that users and maintain a simple level of debug
|
// Log device information to stdout at an 'info' level so that users and maintain a simple level of debug
|
||||||
// output. If the user want more detailed info in a stdout log then the verbosity needs to be turned up to info
|
// output. If the user want more detailed info in a stdout log then the verbosity needs to be turned up to info
|
||||||
// or higher
|
// or higher
|
||||||
logger = logger_info;
|
level = K4A_LOG_LEVEL_INFO;
|
||||||
}
|
}
|
||||||
|
|
||||||
logger(LOGGER_K4A, "******************** Device Info ********************");
|
logger_log(level, __FILE__, __LINE__, "******************** Device Info ********************");
|
||||||
logger(LOGGER_K4A, "K4A SDK version: %s", K4A_VERSION_STR);
|
logger_log(level, __FILE__, __LINE__, "K4A SDK version: %s", K4A_VERSION_STR);
|
||||||
|
|
||||||
char serial_number[128];
|
char serial_number[128];
|
||||||
size_t size = sizeof(serial_number);
|
size_t size = sizeof(serial_number);
|
||||||
if (depthmcu_get_serialnum(depth->depthmcu, serial_number, &size) == K4A_BUFFER_RESULT_SUCCEEDED)
|
if (depthmcu_get_serialnum(depth->depthmcu, serial_number, &size) == K4A_BUFFER_RESULT_SUCCEEDED)
|
||||||
{
|
{
|
||||||
logger(LOGGER_K4A, "Serial Number: %s", serial_number);
|
logger_log(level, __FILE__, __LINE__, "Serial Number: %s", serial_number);
|
||||||
}
|
}
|
||||||
|
|
||||||
k4a_version_t *ver = &depth->version.rgb;
|
k4a_version_t *ver = &depth->version.rgb;
|
||||||
logger(LOGGER_K4A, "RGB Sensor Version: %d.%d.%d", ver->major, ver->minor, ver->iteration);
|
logger_log(level, __FILE__, __LINE__, "RGB Sensor Version: %d.%d.%d", ver->major, ver->minor, ver->iteration);
|
||||||
|
|
||||||
ver = &depth->version.depth;
|
ver = &depth->version.depth;
|
||||||
logger(LOGGER_K4A, "Depth Sensor Version:%d.%d.%d", ver->major, ver->minor, ver->iteration);
|
logger_log(level, __FILE__, __LINE__, "Depth Sensor Version:%d.%d.%d", ver->major, ver->minor, ver->iteration);
|
||||||
|
|
||||||
ver = &depth->version.audio;
|
ver = &depth->version.audio;
|
||||||
logger(LOGGER_K4A, "Mic Array Version: %d.%d.%d", ver->major, ver->minor, ver->iteration);
|
logger_log(level, __FILE__, __LINE__, "Mic Array Version: %d.%d.%d", ver->major, ver->minor, ver->iteration);
|
||||||
|
|
||||||
ver = &depth->version.depth_sensor;
|
ver = &depth->version.depth_sensor;
|
||||||
logger(LOGGER_K4A, "Sensor Config: %d.%d", ver->major, ver->minor);
|
logger_log(level, __FILE__, __LINE__, "Sensor Config: %d.%d", ver->major, ver->minor);
|
||||||
logger(LOGGER_K4A, "Build type: %s", depth->version.firmware_build == 0 ? "Release" : "Debug");
|
logger_log(level,
|
||||||
logger(LOGGER_K4A,
|
__FILE__,
|
||||||
"Signature type: %s",
|
__LINE__,
|
||||||
depth->version.firmware_signature == K4A_FIRMWARE_SIGNATURE_MSFT ?
|
"Build type: %s",
|
||||||
"MSFT" :
|
depth->version.firmware_build == 0 ? "Release" : "Debug");
|
||||||
(depth->version.firmware_signature == K4A_FIRMWARE_SIGNATURE_TEST ? "Test" : "Unsigned"));
|
logger_log(level,
|
||||||
|
__FILE__,
|
||||||
|
__LINE__,
|
||||||
|
"Signature type: %s",
|
||||||
|
depth->version.firmware_signature == K4A_FIRMWARE_SIGNATURE_MSFT ?
|
||||||
|
"MSFT" :
|
||||||
|
(depth->version.firmware_signature == K4A_FIRMWARE_SIGNATURE_TEST ? "Test" : "Unsigned"));
|
||||||
|
|
||||||
logger(LOGGER_K4A, "****************************************************");
|
logger_log(level, __FILE__, __LINE__, "****************************************************");
|
||||||
}
|
}
|
||||||
|
|
||||||
/** see documentation for depthmcu_stream_cb_t
|
/** see documentation for depthmcu_stream_cb_t
|
||||||
|
|
|
@ -57,6 +57,31 @@ typedef enum
|
||||||
DEV_CMD_MAX_CMD // Keep last
|
DEV_CMD_MAX_CMD // Keep last
|
||||||
} device_commands_t;
|
} device_commands_t;
|
||||||
|
|
||||||
|
// This is the status returned by firmware. It is not used, but is great for reporting issues back to firmware
|
||||||
|
// developers
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
DEV_CMD_STATUS_SUCCESS = 0x00000000,
|
||||||
|
DEV_CMD_STATUS_ERROR = 0x00000001,
|
||||||
|
DEV_CMD_STATUS_INVALID_PARAMETER = 0x00000003,
|
||||||
|
DEV_CMD_STATUS_COMMAND_BUSY = 0x00000007,
|
||||||
|
DEV_CMD_STATUS_NOT_IMPLEMENTED = 0x00000008,
|
||||||
|
DEV_CMD_STATUS_OUT_OF_MEMORY = 0x00000009,
|
||||||
|
DEV_CMD_STATUS_PARAM_BAD_TAG = 0x0000000D,
|
||||||
|
DEV_CMD_STATUS_INVALID_PAYLOAD_SIZE = 0x00000012,
|
||||||
|
DEV_CMD_STATUS_FAILED = 0x00000063,
|
||||||
|
DEV_CMD_STATUS_WRONG_COMMAND_STATE = 0x00000101,
|
||||||
|
DEV_CMD_STATUS_WRONG_DEVICE_STATE = 0x00000102,
|
||||||
|
DEV_CMD_STATUS_ADC_INVALID_CHANNEL = 0x00000480,
|
||||||
|
DEV_CMD_STATUS_ADC_INCORRECT_CHANNEL = 0x00000481,
|
||||||
|
DEV_CMD_STATUS_ADC_TIMEOUT = 0x00000482,
|
||||||
|
DEV_CMD_STATUS_ADC_UNKNOWN_DEVICE = 0x00000483,
|
||||||
|
DEV_CMD_STATUS_ADC_UNSUPPORTED_DEVICE = 0x00000484,
|
||||||
|
DEV_CMD_STATUS_ADC_UNSUPPORTED_SIGNAL = 0x00000485,
|
||||||
|
DEV_CMD_STATUS_ADC_INVALID_INPUT = 0x00000486,
|
||||||
|
DEV_CMD_STATUS_ADC_DATA_NOT_AVAILABLE = 0x00000487,
|
||||||
|
} device_command_response_t;
|
||||||
|
|
||||||
/* ---------------------------------------*/
|
/* ---------------------------------------*/
|
||||||
/* DEV_CMD_NV_DATA_GET */
|
/* DEV_CMD_NV_DATA_GET */
|
||||||
/* DEV_CMD_NV_DATA_SET */
|
/* DEV_CMD_NV_DATA_SET */
|
||||||
|
|
|
@ -32,6 +32,7 @@ typedef struct _dewrapper_context_t
|
||||||
THREAD_HANDLE thread;
|
THREAD_HANDLE thread;
|
||||||
LOCK_HANDLE lock;
|
LOCK_HANDLE lock;
|
||||||
COND_HANDLE condition;
|
COND_HANDLE condition;
|
||||||
|
volatile bool thread_started;
|
||||||
volatile bool thread_stop;
|
volatile bool thread_stop;
|
||||||
k4a_result_t thread_start_result;
|
k4a_result_t thread_start_result;
|
||||||
|
|
||||||
|
@ -191,6 +192,7 @@ static int depth_engine_thread(void *param)
|
||||||
// The Start routine is blocked waiting for this thread to complete startup, so we signal it here and share our
|
// The Start routine is blocked waiting for this thread to complete startup, so we signal it here and share our
|
||||||
// startup status.
|
// startup status.
|
||||||
Lock(dewrapper->lock);
|
Lock(dewrapper->lock);
|
||||||
|
dewrapper->thread_started = true;
|
||||||
dewrapper->thread_start_result = result;
|
dewrapper->thread_start_result = result;
|
||||||
Condition_Post(dewrapper->condition);
|
Condition_Post(dewrapper->condition);
|
||||||
Unlock(dewrapper->lock);
|
Unlock(dewrapper->lock);
|
||||||
|
@ -519,6 +521,7 @@ k4a_result_t dewrapper_start(dewrapper_t dewrapper_handle,
|
||||||
dewrapper->fps = config->camera_fps;
|
dewrapper->fps = config->camera_fps;
|
||||||
dewrapper->depth_mode = config->depth_mode;
|
dewrapper->depth_mode = config->depth_mode;
|
||||||
dewrapper->thread_stop = false;
|
dewrapper->thread_stop = false;
|
||||||
|
dewrapper->thread_started = false;
|
||||||
|
|
||||||
THREADAPI_RESULT tresult = ThreadAPI_Create(&dewrapper->thread, depth_engine_thread, dewrapper);
|
THREADAPI_RESULT tresult = ThreadAPI_Create(&dewrapper->thread, depth_engine_thread, dewrapper);
|
||||||
result = K4A_RESULT_FROM_BOOL(tresult == THREADAPI_OK);
|
result = K4A_RESULT_FROM_BOOL(tresult == THREADAPI_OK);
|
||||||
|
@ -527,9 +530,12 @@ k4a_result_t dewrapper_start(dewrapper_t dewrapper_handle,
|
||||||
{
|
{
|
||||||
Lock(dewrapper->lock);
|
Lock(dewrapper->lock);
|
||||||
locked = true;
|
locked = true;
|
||||||
int infinite_timeout = 0;
|
if (!dewrapper->thread_started)
|
||||||
COND_RESULT cond_result = Condition_Wait(dewrapper->condition, dewrapper->lock, infinite_timeout);
|
{
|
||||||
result = K4A_RESULT_FROM_BOOL(cond_result == COND_OK);
|
int infinite_timeout = 0;
|
||||||
|
COND_RESULT cond_result = Condition_Wait(dewrapper->condition, dewrapper->lock, infinite_timeout);
|
||||||
|
result = K4A_RESULT_FROM_BOOL(cond_result == COND_OK);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (K4A_SUCCEEDED(result) && K4A_FAILED(dewrapper->thread_start_result))
|
if (K4A_SUCCEEDED(result) && K4A_FAILED(dewrapper->thread_start_result))
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include <azure_c_shared_utility/envvariable.h>
|
#include <azure_c_shared_utility/envvariable.h>
|
||||||
#include <azure_c_shared_utility/refcount.h>
|
#include <azure_c_shared_utility/refcount.h>
|
||||||
|
#include <azure_c_shared_utility/threadapi.h>
|
||||||
|
|
||||||
// System dependencies
|
// System dependencies
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
@ -27,33 +28,112 @@
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Logger used for capturing errors from this module
|
//
|
||||||
static std::shared_ptr<spdlog::logger> g_logger = NULL;
|
// This logger implements two different loggers, that have different lifespans.
|
||||||
static volatile long g_logger_count = 0;
|
// 1) A streaming callback function can deliver debug messages to a registered callback function.
|
||||||
static bool g_logger_is_file_based = false;
|
// 2) This module and therefore k4a.dll is capable of independently logging to a file or STDOUT through the use of
|
||||||
|
// environment variables defined in k4atypes.h
|
||||||
|
//
|
||||||
|
|
||||||
typedef struct logger_context_t
|
// This is the context for the users logger. Otherwise known as the registered callback function.
|
||||||
|
typedef struct _logger_user_cb_info_t
|
||||||
|
{
|
||||||
|
k4a_logging_message_cb_t *callback;
|
||||||
|
void *callback_context;
|
||||||
|
} logger_user_cb_info_t;
|
||||||
|
|
||||||
|
// This is the context for built in logger that uses env variable to the users logger.
|
||||||
|
typedef struct _logger_context_t
|
||||||
{
|
{
|
||||||
std::shared_ptr<spdlog::logger> logger;
|
std::shared_ptr<spdlog::logger> logger;
|
||||||
} logger_context_t;
|
} logger_context_t;
|
||||||
|
|
||||||
K4A_DECLARE_CONTEXT(logger_t, logger_context_t);
|
K4A_DECLARE_CONTEXT(logger_t, logger_context_t);
|
||||||
|
|
||||||
|
// Logger data for forwarding debug messages to registered callback.
|
||||||
|
static volatile logger_user_cb_info_t *g_user_logger_cb_info = nullptr;
|
||||||
|
static volatile long g_user_logger_cb_info_ref = 0;
|
||||||
|
static k4a_log_level_t g_user_log_level = K4A_LOG_LEVEL_OFF;
|
||||||
|
|
||||||
|
// Logger data used for forwarding debug message to stdout or a dedicated k4a file.
|
||||||
|
static std::shared_ptr<spdlog::logger> g_env_logger = nullptr;
|
||||||
|
static volatile long g_env_logger_count = 0;
|
||||||
|
static bool g_env_logger_is_file_based = false;
|
||||||
|
static k4a_log_level_t g_env_log_level = K4A_LOG_LEVEL_OFF;
|
||||||
|
|
||||||
#define K4A_LOGGER "k4a_logger"
|
#define K4A_LOGGER "k4a_logger"
|
||||||
|
|
||||||
// NOTE, if a sub directory for the log is used, then it needs to be created prior to attempting to create the file
|
// NOTE, if a sub directory for the log is used, then it needs to be created prior to attempting to create the file
|
||||||
#define LOG_FILE_MAX_FILES (3)
|
#define LOG_FILE_MAX_FILES (3)
|
||||||
#define LOG_FILE_EXTENSION ".log"
|
#define LOG_FILE_EXTENSION ".log"
|
||||||
|
|
||||||
|
k4a_result_t logger_register_message_callback(k4a_logging_message_cb_t *message_cb,
|
||||||
|
void *message_cb_context,
|
||||||
|
k4a_log_level_t min_level)
|
||||||
|
{
|
||||||
|
k4a_result_t result = K4A_RESULT_SUCCEEDED;
|
||||||
|
|
||||||
|
if (message_cb)
|
||||||
|
{
|
||||||
|
result = K4A_RESULT_FROM_BOOL(min_level >= K4A_LOG_LEVEL_CRITICAL && min_level <= K4A_LOG_LEVEL_OFF);
|
||||||
|
if (K4A_SUCCEEDED(result))
|
||||||
|
{
|
||||||
|
long count_of_registered_callbacks = INC_REF_VAR(g_user_logger_cb_info_ref);
|
||||||
|
if (count_of_registered_callbacks == 1)
|
||||||
|
{
|
||||||
|
// We won the right to register and create logger_cb_t
|
||||||
|
g_user_logger_cb_info = new logger_user_cb_info_t();
|
||||||
|
g_user_logger_cb_info->callback = message_cb;
|
||||||
|
g_user_logger_cb_info->callback_context = message_cb_context;
|
||||||
|
g_user_log_level = min_level;
|
||||||
|
}
|
||||||
|
else if (g_user_logger_cb_info->callback == message_cb)
|
||||||
|
{
|
||||||
|
// User is calling to update the min_level
|
||||||
|
g_user_log_level = min_level;
|
||||||
|
DEC_REF_VAR(g_user_logger_cb_info_ref);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Disallow the caller from registering a 2nd callback. A new callback can be established by clearing
|
||||||
|
// the existing callback function.
|
||||||
|
result = K4A_RESULT_FROM_BOOL(count_of_registered_callbacks == 1);
|
||||||
|
DEC_REF_VAR(g_user_logger_cb_info_ref);
|
||||||
|
result = K4A_RESULT_FAILED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (g_user_logger_cb_info)
|
||||||
|
{
|
||||||
|
volatile logger_user_cb_info_t *logger_info = g_user_logger_cb_info;
|
||||||
|
g_user_logger_cb_info = nullptr;
|
||||||
|
DEC_REF_VAR(g_user_logger_cb_info_ref);
|
||||||
|
|
||||||
|
// NOTE: this loop could loop forever if user calls start and a new g_user_logger_cb_info is allocated, which
|
||||||
|
// would add a ref to g_user_logger_cb_info_ref. We don't expect there to be races between 1 thread trying to
|
||||||
|
// release the callback function and another thread to add one. To protect against this we would need a more
|
||||||
|
// elaborate locking scheme and the logging functions would having to use it, which would impact performance.
|
||||||
|
while (g_user_logger_cb_info_ref != 0)
|
||||||
|
{
|
||||||
|
// Wait for the parallel calls to logger_log to end
|
||||||
|
ThreadAPI_Sleep(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete logger_info;
|
||||||
|
g_user_log_level = K4A_LOG_LEVEL_OFF;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
k4a_result_t logger_create(logger_config_t *config, logger_t *logger_handle)
|
k4a_result_t logger_create(logger_config_t *config, logger_t *logger_handle)
|
||||||
{
|
{
|
||||||
RETURN_VALUE_IF_ARG(K4A_RESULT_FAILED, config == NULL);
|
RETURN_VALUE_IF_ARG(K4A_RESULT_FAILED, config == NULL);
|
||||||
RETURN_VALUE_IF_ARG(K4A_RESULT_FAILED, logger_handle == NULL);
|
RETURN_VALUE_IF_ARG(K4A_RESULT_FAILED, logger_handle == NULL);
|
||||||
RETURN_VALUE_IF_ARG(K4A_RESULT_FAILED, config->max_log_size == 0);
|
RETURN_VALUE_IF_ARG(K4A_RESULT_FAILED, config->max_log_size == 0);
|
||||||
logger_context_t *context = logger_t_create(logger_handle);
|
logger_context_t *context = logger_t_create(logger_handle);
|
||||||
const char *enable_file_logging = NULL;
|
const char *enable_file_logging = nullptr;
|
||||||
const char *enable_stdout_logging = NULL;
|
const char *enable_stdout_logging = nullptr;
|
||||||
const char *logging_level = NULL;
|
const char *logging_level = nullptr;
|
||||||
|
|
||||||
// environment_get_variable will return null or "\0" if the env var is not set - depends on the OS.
|
// environment_get_variable will return null or "\0" if the env var is not set - depends on the OS.
|
||||||
if (config->env_var_log_to_a_file)
|
if (config->env_var_log_to_a_file)
|
||||||
|
@ -75,15 +155,16 @@ k4a_result_t logger_create(logger_config_t *config, logger_t *logger_handle)
|
||||||
// the post opperation value
|
// the post opperation value
|
||||||
static_assert(0, "This configuration needs to be confirmed");
|
static_assert(0, "This configuration needs to be confirmed");
|
||||||
#endif
|
#endif
|
||||||
if ((INC_REF_VAR(g_logger_count) > 1) || (g_logger))
|
if ((INC_REF_VAR(g_env_logger_count) > 1) || (g_env_logger))
|
||||||
{
|
{
|
||||||
// Reuse the configured logger settings in the event we have more than 1 active handle at a time
|
// Reuse the configured logger settings in the event we have more than 1 active handle at a time
|
||||||
|
context->logger = g_env_logger;
|
||||||
return K4A_RESULT_SUCCEEDED;
|
return K4A_RESULT_SUCCEEDED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (enable_file_logging && enable_file_logging[0] != '\0')
|
if (enable_file_logging && enable_file_logging[0] != '\0')
|
||||||
{
|
{
|
||||||
const char *log_file = NULL;
|
const char *log_file = nullptr;
|
||||||
if (enable_file_logging == NULL || enable_file_logging[0] == '\0')
|
if (enable_file_logging == NULL || enable_file_logging[0] == '\0')
|
||||||
{
|
{
|
||||||
log_file = config->log_file;
|
log_file = config->log_file;
|
||||||
|
@ -108,37 +189,53 @@ k4a_result_t logger_create(logger_config_t *config, logger_t *logger_handle)
|
||||||
|
|
||||||
if (log_file)
|
if (log_file)
|
||||||
{
|
{
|
||||||
// Create a file rotating logger with 5mb size max and 3 rotated files
|
// Create a file rotating logger with 50mb size max and 3 rotated files
|
||||||
g_logger = spdlog::rotating_logger_mt(K4A_LOGGER, log_file, config->max_log_size, LOG_FILE_MAX_FILES);
|
g_env_logger = spdlog::rotating_logger_mt(K4A_LOGGER, log_file, config->max_log_size, LOG_FILE_MAX_FILES);
|
||||||
|
|
||||||
spdlog::set_pattern("%v");
|
spdlog::set_pattern("%v");
|
||||||
g_logger->info("\n\nNew logging session started\n");
|
g_env_logger->info("\n\nNew logging session started\n");
|
||||||
g_logger_is_file_based = true;
|
g_env_logger_is_file_based = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// log to STD out if file logger not created and STDOUT logging is not disabled.
|
// log to stdout if enabled via ENV var AND if file logging is not enabled.
|
||||||
if (g_logger == NULL)
|
if (g_env_logger == NULL)
|
||||||
{
|
{
|
||||||
if (enable_stdout_logging == NULL || enable_stdout_logging[0] == '\0' ||
|
bool enable_stdout_logger = false;
|
||||||
(enable_stdout_logging && enable_stdout_logging[0] != '0'))
|
|
||||||
|
// Default with g_user_logger_cb_info enabled is no stdout logging unless specifically enabled
|
||||||
|
if (enable_stdout_logging && enable_stdout_logging[0] != '0')
|
||||||
{
|
{
|
||||||
// Unable to turn on color logging due to bug with CTest
|
enable_stdout_logger = true;
|
||||||
// https://gitlab.kitware.com/cmake/cmake/issues/17620
|
}
|
||||||
g_logger = spdlog::stdout_logger_mt(K4A_LOGGER);
|
else if (g_user_logger_cb_info == NULL)
|
||||||
|
{
|
||||||
|
// Default with g_user_logger_cb_info disabled is use stdout logging unless specifically disabled
|
||||||
|
if (g_user_logger_cb_info == NULL && (enable_stdout_logging == NULL || enable_stdout_logging[0] == '\0' ||
|
||||||
|
(enable_stdout_logging && enable_stdout_logging[0] != '0')))
|
||||||
|
{
|
||||||
|
enable_stdout_logger = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enable_stdout_logger)
|
||||||
|
{
|
||||||
|
// Unable to turn on color logging due to bug with CTest https://gitlab.kitware.com/cmake/cmake/issues/17620
|
||||||
|
g_env_logger = spdlog::stdout_logger_mt(K4A_LOGGER);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
context->logger = g_logger;
|
|
||||||
|
|
||||||
if (g_logger)
|
if (g_env_logger)
|
||||||
{
|
{
|
||||||
|
context->logger = g_env_logger;
|
||||||
|
g_env_log_level = K4A_LOG_LEVEL_ERROR;
|
||||||
|
|
||||||
//[2018-08-27 10:44:23.218] [level] [threadID] <message>
|
//[2018-08-27 10:44:23.218] [level] [threadID] <message>
|
||||||
// https://github.com/gabime/spdlog/wiki/3.-Custom-formatting
|
// https://github.com/gabime/spdlog/wiki/3.-Custom-formatting
|
||||||
spdlog::set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%^%l%$] [t=%t] %v");
|
spdlog::set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%^%l%$] [t=%t] %v");
|
||||||
|
|
||||||
// Set the default logging level
|
// Set the default logging level SPD will allow. g_env_log_level will furthar refine this.
|
||||||
spdlog::set_level(spdlog::level::err);
|
spdlog::set_level(spdlog::level::trace);
|
||||||
|
|
||||||
// override the default logging level
|
// override the default logging level
|
||||||
if (logging_level && logging_level[0] != '\0')
|
if (logging_level && logging_level[0] != '\0')
|
||||||
|
@ -146,51 +243,54 @@ k4a_result_t logger_create(logger_config_t *config, logger_t *logger_handle)
|
||||||
if (logging_level[0] == 't' || logging_level[0] == 'T')
|
if (logging_level[0] == 't' || logging_level[0] == 'T')
|
||||||
{
|
{
|
||||||
// capture a severity of trace or higher
|
// capture a severity of trace or higher
|
||||||
spdlog::set_level(spdlog::level::trace);
|
g_env_log_level = K4A_LOG_LEVEL_TRACE;
|
||||||
}
|
}
|
||||||
else if (logging_level[0] == 'i' || logging_level[0] == 'I')
|
else if (logging_level[0] == 'i' || logging_level[0] == 'I')
|
||||||
{
|
{
|
||||||
// capture a severity of info or higher
|
// capture a severity of info or higher
|
||||||
spdlog::set_level(spdlog::level::info);
|
g_env_log_level = K4A_LOG_LEVEL_INFO;
|
||||||
}
|
}
|
||||||
else if (logging_level[0] == 'w' || logging_level[0] == 'W')
|
else if (logging_level[0] == 'w' || logging_level[0] == 'W')
|
||||||
{
|
{
|
||||||
// capture a severity of warning or higher
|
// capture a severity of warning or higher
|
||||||
spdlog::set_level(spdlog::level::warn);
|
g_env_log_level = K4A_LOG_LEVEL_WARNING;
|
||||||
}
|
}
|
||||||
else if (logging_level[0] == 'e' || logging_level[0] == 'E')
|
else if (logging_level[0] == 'e' || logging_level[0] == 'E')
|
||||||
{
|
{
|
||||||
// capture a severity of error or higher
|
// capture a severity of error or higher
|
||||||
spdlog::set_level(spdlog::level::err);
|
g_env_log_level = K4A_LOG_LEVEL_ERROR;
|
||||||
}
|
}
|
||||||
else if (logging_level[0] == 'c' || logging_level[0] == 'C')
|
else if (logging_level[0] == 'c' || logging_level[0] == 'C')
|
||||||
{
|
{
|
||||||
// capture a severity of error or higher
|
// capture a severity of error or higher
|
||||||
spdlog::set_level(spdlog::level::critical);
|
g_env_log_level = K4A_LOG_LEVEL_CRITICAL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
g_logger->flush_on(spdlog::level::warn);
|
g_env_logger->flush_on(spdlog::level::warn);
|
||||||
}
|
}
|
||||||
return K4A_RESULT_SUCCEEDED;
|
return K4A_RESULT_SUCCEEDED;
|
||||||
}
|
}
|
||||||
|
|
||||||
void logger_destroy(logger_t logger_handle)
|
void logger_destroy(logger_t logger_handle)
|
||||||
{
|
{
|
||||||
|
RETURN_VALUE_IF_HANDLE_INVALID(VOID_VALUE, logger_t, logger_handle);
|
||||||
|
|
||||||
logger_context_t *context = logger_t_get_context(logger_handle);
|
logger_context_t *context = logger_t_get_context(logger_handle);
|
||||||
|
|
||||||
// destroy the logger
|
// Destroy the logger
|
||||||
if (DEC_REF_VAR(g_logger_count) == 0)
|
if (DEC_REF_VAR(g_env_logger_count) == 0)
|
||||||
{
|
{
|
||||||
bool drop_logger = g_logger != NULL;
|
bool drop_logger = g_env_logger != NULL;
|
||||||
g_logger = NULL;
|
g_env_logger = NULL;
|
||||||
if (drop_logger)
|
if (drop_logger)
|
||||||
{
|
{
|
||||||
spdlog::drop(K4A_LOGGER);
|
spdlog::drop(K4A_LOGGER);
|
||||||
}
|
}
|
||||||
g_logger_is_file_based = false;
|
g_env_logger_is_file_based = false;
|
||||||
|
g_env_log_level = K4A_LOG_LEVEL_OFF;
|
||||||
}
|
}
|
||||||
context->logger = NULL;
|
context->logger = nullptr;
|
||||||
|
|
||||||
logger_t_destroy(logger_handle);
|
logger_t_destroy(logger_handle);
|
||||||
}
|
}
|
||||||
|
@ -199,125 +299,76 @@ void logger_destroy(logger_t logger_handle)
|
||||||
// Enable printf type checking in clang and gcc
|
// Enable printf type checking in clang and gcc
|
||||||
__attribute__((__format__ (__printf__, 2, 0)))
|
__attribute__((__format__ (__printf__, 2, 0)))
|
||||||
#endif
|
#endif
|
||||||
void logger_trace(
|
void logger_log(k4a_log_level_t level, const char * file, const int line, const char *format, ...)
|
||||||
const char * zone,
|
|
||||||
const char * format,
|
|
||||||
...)
|
|
||||||
{
|
{
|
||||||
char buffer[1024];
|
// Quick exit if we are not logging the message
|
||||||
|
if (level > g_env_log_level && level > g_user_log_level)
|
||||||
if (g_logger == NULL)
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
va_list args;
|
if (g_env_logger || g_user_logger_cb_info)
|
||||||
va_start(args, format);
|
{
|
||||||
vsnprintf(buffer, sizeof(buffer), format, args);
|
char buffer[1024];
|
||||||
|
va_list args;
|
||||||
g_logger->trace("[{0}] {1}", zone, buffer);
|
va_start(args, format);
|
||||||
va_end(args);
|
#ifndef _WIN32
|
||||||
}
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
|
||||||
#if defined(__GNUC__) || defined(__clang__)
|
|
||||||
// Enable printf type checking in clang and gcc
|
|
||||||
__attribute__((__format__ (__printf__, 2, 0)))
|
|
||||||
#endif
|
#endif
|
||||||
void logger_info(
|
vsnprintf(buffer, sizeof(buffer), format, args);
|
||||||
const char * zone,
|
#ifndef _WIN32
|
||||||
const char * format,
|
#pragma GCC diagnostic pop
|
||||||
...)
|
|
||||||
{
|
|
||||||
char buffer[1024];
|
|
||||||
|
|
||||||
if (g_logger == NULL)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
va_list args;
|
|
||||||
va_start(args, format);
|
|
||||||
vsnprintf(buffer, sizeof(buffer), format, args);
|
|
||||||
|
|
||||||
g_logger->info("[{0}] {1}", zone, buffer);
|
|
||||||
va_end(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(__GNUC__) || defined(__clang__)
|
|
||||||
// Enable printf type checking in clang and gcc
|
|
||||||
__attribute__((__format__ (__printf__, 2, 0)))
|
|
||||||
#endif
|
#endif
|
||||||
void logger_warn(
|
va_end(args);
|
||||||
const char * zone,
|
|
||||||
const char * format,
|
|
||||||
...)
|
|
||||||
{
|
|
||||||
char buffer[1024];
|
|
||||||
|
|
||||||
if (g_logger == NULL)
|
if ((level <= g_user_log_level) && (g_user_log_level != K4A_LOG_LEVEL_OFF))
|
||||||
{
|
{
|
||||||
return;
|
// must ++ before getting the shared pointer, or the wait in releasing the callback function can return
|
||||||
|
// without waiting for all pending instances using this context to complete.
|
||||||
|
INC_REF_VAR(g_user_logger_cb_info_ref);
|
||||||
|
volatile logger_user_cb_info_t *logger_cb = g_user_logger_cb_info;
|
||||||
|
if (logger_cb)
|
||||||
|
{
|
||||||
|
logger_cb->callback(logger_cb->callback_context, level, file, line, buffer);
|
||||||
|
logger_cb = nullptr;
|
||||||
|
}
|
||||||
|
DEC_REF_VAR(g_user_logger_cb_info_ref);
|
||||||
|
}
|
||||||
|
if ((level <= g_env_log_level) && (g_env_log_level != K4A_LOG_LEVEL_OFF))
|
||||||
|
{
|
||||||
|
// Keep a copy of the logger around while we add this entry
|
||||||
|
std::shared_ptr<spdlog::logger> logger = g_env_logger;
|
||||||
|
if (logger)
|
||||||
|
{
|
||||||
|
switch (level)
|
||||||
|
{
|
||||||
|
case K4A_LOG_LEVEL_CRITICAL:
|
||||||
|
logger->critical("{0} ({1}): {2}", file, line, buffer);
|
||||||
|
break;
|
||||||
|
case K4A_LOG_LEVEL_ERROR:
|
||||||
|
logger->error("{0} ({1}): {2}", file, line, buffer);
|
||||||
|
break;
|
||||||
|
case K4A_LOG_LEVEL_WARNING:
|
||||||
|
logger->warn("{0} ({1}): {2}", file, line, buffer);
|
||||||
|
break;
|
||||||
|
case K4A_LOG_LEVEL_INFO:
|
||||||
|
logger->info("{0} ({1}): {2}", file, line, buffer);
|
||||||
|
break;
|
||||||
|
case K4A_LOG_LEVEL_TRACE:
|
||||||
|
default:
|
||||||
|
logger->trace("{0} ({1}): {2}", file, line, buffer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logger = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
va_list args;
|
|
||||||
va_start(args, format);
|
|
||||||
vsnprintf(buffer, sizeof(buffer), format, args);
|
|
||||||
|
|
||||||
g_logger->warn("[{0}] {1}", zone, buffer);
|
|
||||||
va_end(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(__GNUC__) || defined(__clang__)
|
|
||||||
// Enable printf type checking in clang and gcc
|
|
||||||
__attribute__((__format__ (__printf__, 2, 0)))
|
|
||||||
#endif
|
|
||||||
void logger_error(
|
|
||||||
const char * zone,
|
|
||||||
const char * format,
|
|
||||||
...)
|
|
||||||
{
|
|
||||||
char buffer[1024];
|
|
||||||
|
|
||||||
if (g_logger == NULL)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
va_list args;
|
|
||||||
va_start(args, format);
|
|
||||||
vsnprintf(buffer, sizeof(buffer), format, args);
|
|
||||||
|
|
||||||
g_logger->error("[{0}] {1}", zone, buffer);
|
|
||||||
va_end(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(__GNUC__) || defined(__clang__)
|
|
||||||
// Enable printf type checking in clang and gcc
|
|
||||||
__attribute__((__format__ (__printf__, 2, 0)))
|
|
||||||
#endif
|
|
||||||
void logger_critical(
|
|
||||||
const char * zone,
|
|
||||||
const char * format,
|
|
||||||
...)
|
|
||||||
{
|
|
||||||
char buffer[1024];
|
|
||||||
|
|
||||||
if (g_logger == NULL)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
va_list args;
|
|
||||||
va_start(args, format);
|
|
||||||
vsnprintf(buffer, sizeof(buffer), format, args);
|
|
||||||
|
|
||||||
g_logger->critical("[{0}] {1}", zone, buffer);
|
|
||||||
va_end(args);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool logger_is_file_based()
|
bool logger_is_file_based()
|
||||||
{
|
{
|
||||||
return g_logger_is_file_based;
|
return g_env_logger_is_file_based;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|
|
@ -31,6 +31,8 @@ target_link_libraries(k4a_playback PUBLIC
|
||||||
k4ainternal::logging
|
k4ainternal::logging
|
||||||
ebml::ebml
|
ebml::ebml
|
||||||
matroska::matroska
|
matroska::matroska
|
||||||
|
libyuv::libyuv
|
||||||
|
libjpeg-turbo::libjpeg-turbo
|
||||||
)
|
)
|
||||||
|
|
||||||
# Define alias for other targets to link against
|
# Define alias for other targets to link against
|
||||||
|
|
|
@ -12,6 +12,9 @@
|
||||||
#include <k4ainternal/common.h>
|
#include <k4ainternal/common.h>
|
||||||
#include <k4ainternal/logging.h>
|
#include <k4ainternal/logging.h>
|
||||||
|
|
||||||
|
#include <turbojpeg.h>
|
||||||
|
#include <libyuv.h>
|
||||||
|
|
||||||
using namespace LIBMATROSKA_NAMESPACE;
|
using namespace LIBMATROSKA_NAMESPACE;
|
||||||
|
|
||||||
namespace k4arecord
|
namespace k4arecord
|
||||||
|
@ -24,6 +27,22 @@ std::unique_ptr<EbmlElement> next_child(k4a_playback_context_t *context, EbmlEle
|
||||||
EbmlElement *element =
|
EbmlElement *element =
|
||||||
context->stream->FindNextElement(parent->Generic().Context, upper_level, parent->GetSize(), false, 0);
|
context->stream->FindNextElement(parent->Generic().Context, upper_level, parent->GetSize(), false, 0);
|
||||||
|
|
||||||
|
// upper_level shows the relationship of the element to the parent element
|
||||||
|
// -1 : global element
|
||||||
|
// 0 : child
|
||||||
|
// 1 : same level
|
||||||
|
// + : further parent
|
||||||
|
if (upper_level > 0)
|
||||||
|
{
|
||||||
|
// This element is not a child of the parent, set the file pointer back to the start of the element and
|
||||||
|
// return nullptr.
|
||||||
|
uint64_t file_offset = element->GetElementPosition();
|
||||||
|
assert(file_offset <= INT64_MAX);
|
||||||
|
context->ebml_file->setFilePointer((int64_t)file_offset);
|
||||||
|
delete element;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
return std::unique_ptr<EbmlElement>(element);
|
return std::unique_ptr<EbmlElement>(element);
|
||||||
}
|
}
|
||||||
catch (std::ios_base::failure &e)
|
catch (std::ios_base::failure &e)
|
||||||
|
@ -220,17 +239,17 @@ k4a_result_t parse_mkv(k4a_playback_context_t *context)
|
||||||
|
|
||||||
KaxSimpleBlock *simple_block = NULL;
|
KaxSimpleBlock *simple_block = NULL;
|
||||||
KaxBlockGroup *block_group = NULL;
|
KaxBlockGroup *block_group = NULL;
|
||||||
std::shared_ptr<loaded_cluster_t> last_cluster = load_cluster(context, cluster_info);
|
std::shared_ptr<KaxCluster> last_cluster = load_cluster_internal(context, cluster_info);
|
||||||
if (last_cluster == nullptr || last_cluster->cluster == nullptr)
|
if (last_cluster == nullptr)
|
||||||
{
|
{
|
||||||
LOG_ERROR("Failed to load end of recording.", 0);
|
LOG_ERROR("Failed to load end of recording.", 0);
|
||||||
return K4A_RESULT_FAILED;
|
return K4A_RESULT_FAILED;
|
||||||
}
|
}
|
||||||
for (EbmlElement *e : last_cluster->cluster->GetElementList())
|
for (EbmlElement *e : last_cluster->GetElementList())
|
||||||
{
|
{
|
||||||
if (check_element_type(e, &simple_block))
|
if (check_element_type(e, &simple_block))
|
||||||
{
|
{
|
||||||
simple_block->SetParent(*last_cluster->cluster);
|
simple_block->SetParent(*last_cluster);
|
||||||
uint64_t block_timestamp_ns = simple_block->GlobalTimecode();
|
uint64_t block_timestamp_ns = simple_block->GlobalTimecode();
|
||||||
if (block_timestamp_ns > context->last_timestamp_ns)
|
if (block_timestamp_ns > context->last_timestamp_ns)
|
||||||
{
|
{
|
||||||
|
@ -239,7 +258,7 @@ k4a_result_t parse_mkv(k4a_playback_context_t *context)
|
||||||
}
|
}
|
||||||
else if (check_element_type(e, &block_group))
|
else if (check_element_type(e, &block_group))
|
||||||
{
|
{
|
||||||
block_group->SetParent(*last_cluster->cluster);
|
block_group->SetParent(*last_cluster);
|
||||||
|
|
||||||
KaxTrackEntry *parent_track = NULL;
|
KaxTrackEntry *parent_track = NULL;
|
||||||
for (EbmlElement *e2 : context->tracks->GetElementList())
|
for (EbmlElement *e2 : context->tracks->GetElementList())
|
||||||
|
@ -374,26 +393,15 @@ k4a_result_t parse_recording_config(k4a_playback_context_t *context)
|
||||||
|
|
||||||
context->timecode_scale = GetChild<KaxTimecodeScale>(*context->segment_info).GetValue();
|
context->timecode_scale = GetChild<KaxTimecodeScale>(*context->segment_info).GetValue();
|
||||||
|
|
||||||
context->color_track.track = get_track_by_tag(context, "K4A_COLOR_MODE");
|
context->color_track.track = find_track(context, "COLOR", "K4A_COLOR_TRACK");
|
||||||
context->depth_track.track = get_track_by_tag(context, "K4A_DEPTH_MODE");
|
context->depth_track.track = find_track(context, "DEPTH", "K4A_DEPTH_TRACK");
|
||||||
context->ir_track.track = get_track_by_tag(context, "K4A_IR_MODE");
|
context->ir_track.track = find_track(context, "IR", "K4A_IR_TRACK");
|
||||||
context->imu_track.track = get_track_by_tag(context, "K4A_IMU_MODE");
|
|
||||||
if (context->color_track.track == NULL)
|
|
||||||
{
|
|
||||||
context->color_track.track = get_track_by_name(context, "COLOR");
|
|
||||||
}
|
|
||||||
if (context->depth_track.track == NULL)
|
|
||||||
{
|
|
||||||
context->depth_track.track = get_track_by_name(context, "DEPTH");
|
|
||||||
}
|
|
||||||
if (context->ir_track.track == NULL)
|
if (context->ir_track.track == NULL)
|
||||||
{
|
{
|
||||||
context->ir_track.track = get_track_by_name(context, "IR");
|
// Support legacy IR track naming.
|
||||||
}
|
context->ir_track.track = find_track(context, "DEPTH_IR", NULL);
|
||||||
if (context->imu_track.track == NULL)
|
|
||||||
{
|
|
||||||
context->imu_track.track = get_track_by_name(context, "IMU");
|
|
||||||
}
|
}
|
||||||
|
context->imu_track.track = find_track(context, "IMU", "K4A_IMU_TRACK");
|
||||||
|
|
||||||
// Read device calibration attachment
|
// Read device calibration attachment
|
||||||
context->calibration_attachment = get_attachment_by_tag(context, "K4A_CALIBRATION_FILE");
|
context->calibration_attachment = get_attachment_by_tag(context, "K4A_CALIBRATION_FILE");
|
||||||
|
@ -438,12 +446,14 @@ k4a_result_t parse_recording_config(k4a_playback_context_t *context)
|
||||||
|
|
||||||
context->record_config.color_track_enabled = true;
|
context->record_config.color_track_enabled = true;
|
||||||
context->record_config.color_format = context->color_track.format;
|
context->record_config.color_format = context->color_track.format;
|
||||||
|
context->color_format_conversion = context->color_track.format;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
context->record_config.color_resolution = K4A_COLOR_RESOLUTION_OFF;
|
context->record_config.color_resolution = K4A_COLOR_RESOLUTION_OFF;
|
||||||
// Set to a default color format if color track is disabled.
|
// Set to a default color format if color track is disabled.
|
||||||
context->record_config.color_format = K4A_IMAGE_FORMAT_CUSTOM;
|
context->record_config.color_format = K4A_IMAGE_FORMAT_CUSTOM;
|
||||||
|
context->color_format_conversion = K4A_IMAGE_FORMAT_CUSTOM;
|
||||||
}
|
}
|
||||||
|
|
||||||
KaxTag *depth_mode_tag = get_tag(context, "K4A_DEPTH_MODE");
|
KaxTag *depth_mode_tag = get_tag(context, "K4A_DEPTH_MODE");
|
||||||
|
@ -452,26 +462,48 @@ k4a_result_t parse_recording_config(k4a_playback_context_t *context)
|
||||||
LOG_ERROR("K4A_DEPTH_MODE tag is missing.", 0);
|
LOG_ERROR("K4A_DEPTH_MODE tag is missing.", 0);
|
||||||
return K4A_RESULT_FAILED;
|
return K4A_RESULT_FAILED;
|
||||||
}
|
}
|
||||||
std::string depth_mode_str = get_tag_string(depth_mode_tag);
|
|
||||||
|
|
||||||
|
std::string depth_mode_str;
|
||||||
uint32_t depth_width = 0;
|
uint32_t depth_width = 0;
|
||||||
uint32_t depth_height = 0;
|
uint32_t depth_height = 0;
|
||||||
context->record_config.depth_mode = K4A_DEPTH_MODE_OFF;
|
context->record_config.depth_mode = K4A_DEPTH_MODE_OFF;
|
||||||
for (size_t i = 0; i < arraysize(depth_modes); i++)
|
|
||||||
|
if (depth_mode_tag != NULL)
|
||||||
{
|
{
|
||||||
if (k4a_convert_depth_mode_to_width_height(depth_modes[i].first, &depth_width, &depth_height))
|
depth_mode_str = get_tag_string(depth_mode_tag);
|
||||||
|
for (size_t i = 0; i < arraysize(depth_modes); i++)
|
||||||
{
|
{
|
||||||
if (depth_mode_str == depth_modes[i].second)
|
if (depth_mode_str == depth_modes[i].second)
|
||||||
{
|
{
|
||||||
context->record_config.depth_mode = depth_modes[i].first;
|
if (k4a_convert_depth_mode_to_width_height(depth_modes[i].first, &depth_width, &depth_height))
|
||||||
break;
|
{
|
||||||
|
context->record_config.depth_mode = depth_modes[i].first;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
if (context->record_config.depth_mode == K4A_DEPTH_MODE_OFF)
|
||||||
if (context->record_config.depth_mode == K4A_DEPTH_MODE_OFF)
|
{
|
||||||
{
|
// Try to find the mode matching strings in the legacy modes
|
||||||
LOG_ERROR("Unsupported depth mode: %s", depth_mode_str.c_str());
|
for (size_t i = 0; i < arraysize(legacy_depth_modes); i++)
|
||||||
return K4A_RESULT_FAILED;
|
{
|
||||||
|
if (depth_mode_str == legacy_depth_modes[i].second)
|
||||||
|
{
|
||||||
|
if (k4a_convert_depth_mode_to_width_height(legacy_depth_modes[i].first,
|
||||||
|
&depth_width,
|
||||||
|
&depth_height))
|
||||||
|
{
|
||||||
|
context->record_config.depth_mode = legacy_depth_modes[i].first;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (context->record_config.depth_mode == K4A_DEPTH_MODE_OFF)
|
||||||
|
{
|
||||||
|
LOG_ERROR("Unsupported depth mode: %s", depth_mode_str.c_str());
|
||||||
|
return K4A_RESULT_FAILED;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context->depth_track.track)
|
if (context->depth_track.track)
|
||||||
|
@ -547,6 +579,10 @@ k4a_result_t parse_recording_config(k4a_playback_context_t *context)
|
||||||
}
|
}
|
||||||
|
|
||||||
RETURN_IF_ERROR(read_bitmap_info_header(&context->ir_track));
|
RETURN_IF_ERROR(read_bitmap_info_header(&context->ir_track));
|
||||||
|
if (context->ir_track.format == K4A_IMAGE_FORMAT_DEPTH16)
|
||||||
|
{
|
||||||
|
context->ir_track.format = K4A_IMAGE_FORMAT_IR16;
|
||||||
|
}
|
||||||
|
|
||||||
context->record_config.ir_track_enabled = true;
|
context->record_config.ir_track_enabled = true;
|
||||||
}
|
}
|
||||||
|
@ -762,59 +798,66 @@ void reset_seek_pointers(k4a_playback_context_t *context, uint64_t seek_timestam
|
||||||
context->imu_sample_index = -1;
|
context->imu_sample_index = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
KaxTrackEntry *get_track_by_name(k4a_playback_context_t *context, const char *name)
|
KaxTrackEntry *find_track(k4a_playback_context_t *context, const char *name, const char *tag_name)
|
||||||
{
|
{
|
||||||
RETURN_VALUE_IF_ARG(NULL, context == NULL);
|
RETURN_VALUE_IF_ARG(NULL, context == NULL);
|
||||||
RETURN_VALUE_IF_ARG(NULL, context->tracks == nullptr);
|
RETURN_VALUE_IF_ARG(NULL, context->tracks == nullptr);
|
||||||
RETURN_VALUE_IF_ARG(NULL, name == NULL);
|
RETURN_VALUE_IF_ARG(NULL, name == NULL);
|
||||||
|
|
||||||
std::string search(name);
|
std::string search_name(name);
|
||||||
KaxTrackEntry *track = NULL;
|
uint64_t search_uid = 0;
|
||||||
for (EbmlElement *e : context->tracks->GetElementList())
|
|
||||||
|
if (tag_name != NULL)
|
||||||
{
|
{
|
||||||
if (check_element_type(e, &track))
|
KaxTag *track_tag = get_tag(context, tag_name);
|
||||||
|
if (track_tag)
|
||||||
{
|
{
|
||||||
if (GetChild<KaxTrackName>(*track).GetValueUTF8() == search)
|
KaxTagTargets &tagTargets = GetChild<KaxTagTargets>(*track_tag);
|
||||||
|
if (GetChild<KaxTagTargetType>(tagTargets).GetValue() == "TRACK")
|
||||||
{
|
{
|
||||||
track->SetGlobalTimecodeScale(context->timecode_scale);
|
search_uid = GetChild<KaxTagTrackUID>(tagTargets).GetValue();
|
||||||
return track;
|
|
||||||
}
|
}
|
||||||
}
|
if (search_uid == 0)
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
KaxTrackEntry *get_track_by_tag(k4a_playback_context_t *context, const char *tag_name)
|
|
||||||
{
|
|
||||||
RETURN_VALUE_IF_ARG(NULL, context == NULL);
|
|
||||||
RETURN_VALUE_IF_ARG(NULL, context->tracks == NULL);
|
|
||||||
RETURN_VALUE_IF_ARG(NULL, tag_name == NULL);
|
|
||||||
|
|
||||||
KaxTag *track_tag = get_tag(context, tag_name);
|
|
||||||
if (track_tag)
|
|
||||||
{
|
|
||||||
KaxTagTargets &tagTargets = GetChild<KaxTagTargets>(*track_tag);
|
|
||||||
if (GetChild<KaxTagTargetType>(tagTargets).GetValue() != "TRACK")
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
uint64_t search_uid = GetChild<KaxTagTrackUID>(tagTargets).GetValue();
|
|
||||||
|
|
||||||
KaxTrackEntry *track = NULL;
|
|
||||||
for (EbmlElement *e : context->tracks->GetElementList())
|
|
||||||
{
|
|
||||||
if (check_element_type(e, &track))
|
|
||||||
{
|
{
|
||||||
if (GetChild<KaxTrackUID>(*track).GetValue() == search_uid)
|
std::istringstream search_uid_str(get_tag_string(track_tag));
|
||||||
|
search_uid_str >> search_uid;
|
||||||
|
if (search_uid_str.fail())
|
||||||
{
|
{
|
||||||
track->SetGlobalTimecodeScale(context->timecode_scale);
|
LOG_ERROR("Track tag '%s' for track %s is not valid.", tag_name, name);
|
||||||
return track;
|
search_uid = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KaxTrackEntry *track_found = NULL;
|
||||||
|
for (EbmlElement *e : context->tracks->GetElementList())
|
||||||
|
{
|
||||||
|
KaxTrackEntry *track = NULL;
|
||||||
|
if (check_element_type(e, &track))
|
||||||
|
{
|
||||||
|
std::string track_name = GetChild<KaxTrackName>(*track).GetValueUTF8();
|
||||||
|
uint64_t track_uid = GetChild<KaxTrackUID>(*track).GetValue();
|
||||||
|
std::string track_codec_id = GetChild<KaxCodecID>(*track).GetValue();
|
||||||
|
|
||||||
|
if (search_uid != 0 && track_uid == search_uid)
|
||||||
|
{
|
||||||
|
track_found = track;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (track_name == search_name)
|
||||||
|
{
|
||||||
|
track_found = track;
|
||||||
|
// Search by UID has priority over search by name, keep searching for a matching UID.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (track_found != NULL)
|
||||||
|
{
|
||||||
|
track_found->SetGlobalTimecodeScale(context->timecode_scale);
|
||||||
|
return track_found;
|
||||||
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1166,7 +1209,7 @@ cluster_info_t *next_cluster(k4a_playback_context_t *context, cluster_info_t *cu
|
||||||
|
|
||||||
// Load a cluster from the cluster cache / disk without any neighbor preloading.
|
// Load a cluster from the cluster cache / disk without any neighbor preloading.
|
||||||
// This should never fail unless there is a file IO error.
|
// This should never fail unless there is a file IO error.
|
||||||
static std::shared_ptr<KaxCluster> load_cluster_internal(k4a_playback_context_t *context, cluster_info_t *cluster_info)
|
std::shared_ptr<KaxCluster> load_cluster_internal(k4a_playback_context_t *context, cluster_info_t *cluster_info)
|
||||||
{
|
{
|
||||||
RETURN_VALUE_IF_ARG(nullptr, context == NULL);
|
RETURN_VALUE_IF_ARG(nullptr, context == NULL);
|
||||||
RETURN_VALUE_IF_ARG(nullptr, context->ebml_file == nullptr);
|
RETURN_VALUE_IF_ARG(nullptr, context->ebml_file == nullptr);
|
||||||
|
@ -1518,6 +1561,206 @@ static void free_vector_buffer(void *buffer, void *context)
|
||||||
delete vector;
|
delete vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Allocates a new image in the specified format from in_block
|
||||||
|
k4a_result_t convert_block_to_image(k4a_playback_context_t *context,
|
||||||
|
block_info_t *in_block,
|
||||||
|
k4a_image_t *image_out,
|
||||||
|
k4a_image_format_t target_format)
|
||||||
|
{
|
||||||
|
RETURN_VALUE_IF_ARG(K4A_RESULT_FAILED, context == NULL);
|
||||||
|
RETURN_VALUE_IF_ARG(K4A_RESULT_FAILED, in_block == nullptr);
|
||||||
|
RETURN_VALUE_IF_ARG(K4A_RESULT_FAILED, image_out == nullptr);
|
||||||
|
RETURN_VALUE_IF_ARG(K4A_RESULT_FAILED, in_block->reader == NULL);
|
||||||
|
RETURN_VALUE_IF_ARG(K4A_RESULT_FAILED, in_block->block == NULL);
|
||||||
|
RETURN_VALUE_IF_ARG(K4A_RESULT_FAILED, in_block->block->NumberFrames() != 1);
|
||||||
|
|
||||||
|
DataBuffer &data_buffer = in_block->block->GetBuffer(0);
|
||||||
|
|
||||||
|
k4a_result_t result = K4A_RESULT_SUCCEEDED;
|
||||||
|
std::vector<uint8_t> *buffer = NULL;
|
||||||
|
assert(in_block->reader->width <= INT_MAX);
|
||||||
|
assert(in_block->reader->height <= INT_MAX);
|
||||||
|
assert(in_block->reader->stride <= INT_MAX);
|
||||||
|
int out_width = (int)in_block->reader->width;
|
||||||
|
int out_height = (int)in_block->reader->height;
|
||||||
|
int out_stride = (int)in_block->reader->stride;
|
||||||
|
assert(out_height >= 0 && out_width >= 0);
|
||||||
|
|
||||||
|
switch (target_format)
|
||||||
|
{
|
||||||
|
case K4A_IMAGE_FORMAT_DEPTH16:
|
||||||
|
case K4A_IMAGE_FORMAT_IR16:
|
||||||
|
buffer = new std::vector<uint8_t>(data_buffer.Buffer(), data_buffer.Buffer() + data_buffer.Size());
|
||||||
|
if (in_block->reader->format == K4A_IMAGE_FORMAT_DEPTH16 || in_block->reader->format == K4A_IMAGE_FORMAT_IR16)
|
||||||
|
{
|
||||||
|
// 16 bit grayscale needs to be converted from big-endian back to little-endian.
|
||||||
|
assert(buffer->size() % sizeof(uint16_t) == 0);
|
||||||
|
uint16_t *buffer_raw = reinterpret_cast<uint16_t *>(buffer->data());
|
||||||
|
size_t buffer_size = buffer->size() / sizeof(uint16_t);
|
||||||
|
for (size_t i = 0; i < buffer_size; i++)
|
||||||
|
{
|
||||||
|
buffer_raw[i] = swap_bytes_16(buffer_raw[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (in_block->reader->format == K4A_IMAGE_FORMAT_COLOR_YUY2)
|
||||||
|
{
|
||||||
|
// For backwards compatibility with early recordings, the YUY2 format was used. The actual data buffer is
|
||||||
|
// 16-bit little-endian, so we can just use the buffer as-is.
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG_ERROR("Unsupported image format conversion: %d to %d", in_block->reader->format, target_format);
|
||||||
|
result = K4A_RESULT_FAILED;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case K4A_IMAGE_FORMAT_COLOR_MJPG:
|
||||||
|
case K4A_IMAGE_FORMAT_COLOR_NV12:
|
||||||
|
case K4A_IMAGE_FORMAT_COLOR_YUY2:
|
||||||
|
case K4A_IMAGE_FORMAT_COLOR_BGRA32:
|
||||||
|
if (in_block->reader->format == target_format)
|
||||||
|
{
|
||||||
|
// No format conversion is required, just copy the buffer.
|
||||||
|
buffer = new std::vector<uint8_t>(data_buffer.Buffer(), data_buffer.Buffer() + data_buffer.Size());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Convert the buffer to BGRA format first
|
||||||
|
out_stride = out_width * 4 * (int)sizeof(uint8_t);
|
||||||
|
buffer = new std::vector<uint8_t>((size_t)(out_height * out_stride));
|
||||||
|
|
||||||
|
if (in_block->reader->format == K4A_IMAGE_FORMAT_COLOR_MJPG)
|
||||||
|
{
|
||||||
|
tjhandle turbojpeg_handle = tjInitDecompress();
|
||||||
|
if (tjDecompress2(turbojpeg_handle,
|
||||||
|
data_buffer.Buffer(),
|
||||||
|
data_buffer.Size(),
|
||||||
|
buffer->data(),
|
||||||
|
out_width,
|
||||||
|
0, // pitch
|
||||||
|
out_height,
|
||||||
|
TJPF_BGRA,
|
||||||
|
TJFLAG_FASTDCT | TJFLAG_FASTUPSAMPLE) != 0)
|
||||||
|
{
|
||||||
|
LOG_ERROR("Failed to decompress jpeg image to BGRA format.", 0);
|
||||||
|
result = K4A_RESULT_FAILED;
|
||||||
|
}
|
||||||
|
(void)tjDestroy(turbojpeg_handle);
|
||||||
|
}
|
||||||
|
else if (in_block->reader->format == K4A_IMAGE_FORMAT_COLOR_NV12)
|
||||||
|
{
|
||||||
|
// The endianness of libyuv's ARGB is opposite our BGRA format. They are the same byte order.
|
||||||
|
if (libyuv::NV12ToARGB(data_buffer.Buffer(),
|
||||||
|
(int)in_block->reader->stride,
|
||||||
|
data_buffer.Buffer() + (out_height * (int)in_block->reader->stride),
|
||||||
|
(int)in_block->reader->stride,
|
||||||
|
buffer->data(),
|
||||||
|
out_stride,
|
||||||
|
out_width,
|
||||||
|
out_height) != 0)
|
||||||
|
{
|
||||||
|
LOG_ERROR("Failed to convert NV12 image to BGRA format.", 0);
|
||||||
|
result = K4A_RESULT_FAILED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (in_block->reader->format == K4A_IMAGE_FORMAT_COLOR_YUY2)
|
||||||
|
{
|
||||||
|
// The endianness of libyuv's ARGB is opposite our BGRA format. They are the same byte order.
|
||||||
|
if (libyuv::YUY2ToARGB(data_buffer.Buffer(),
|
||||||
|
(int)in_block->reader->stride,
|
||||||
|
buffer->data(),
|
||||||
|
out_stride,
|
||||||
|
out_width,
|
||||||
|
out_height) != 0)
|
||||||
|
{
|
||||||
|
LOG_ERROR("Failed to convert YUY2 image to BGRA format.", 0);
|
||||||
|
result = K4A_RESULT_FAILED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG_ERROR("Unsupported image format conversion: %d to %d", in_block->reader->format, target_format);
|
||||||
|
result = K4A_RESULT_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (K4A_SUCCEEDED(result) && target_format != K4A_IMAGE_FORMAT_COLOR_BGRA32)
|
||||||
|
{
|
||||||
|
auto bgra_buffer = buffer;
|
||||||
|
buffer = NULL;
|
||||||
|
int bgra_stride = out_stride;
|
||||||
|
|
||||||
|
if (target_format == K4A_IMAGE_FORMAT_COLOR_NV12)
|
||||||
|
{
|
||||||
|
out_stride = out_width;
|
||||||
|
size_t y_plane_size = (size_t)(out_height * out_stride);
|
||||||
|
// Round up the size of the UV plane in case the resolution is odd.
|
||||||
|
size_t uv_plane_size = (size_t)(out_height * out_stride + 1) / 2;
|
||||||
|
buffer = new std::vector<uint8_t>(y_plane_size + uv_plane_size);
|
||||||
|
|
||||||
|
if (libyuv::ARGBToNV12(bgra_buffer->data(),
|
||||||
|
bgra_stride,
|
||||||
|
buffer->data(),
|
||||||
|
out_stride,
|
||||||
|
buffer->data() + y_plane_size,
|
||||||
|
out_stride,
|
||||||
|
out_width,
|
||||||
|
out_height) != 0)
|
||||||
|
{
|
||||||
|
LOG_ERROR("Failed to convert BGRA image to NV12 format.", 0);
|
||||||
|
result = K4A_RESULT_FAILED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (target_format == K4A_IMAGE_FORMAT_COLOR_YUY2)
|
||||||
|
{
|
||||||
|
out_stride = out_width * 2;
|
||||||
|
buffer = new std::vector<uint8_t>((size_t)(out_height * out_stride));
|
||||||
|
|
||||||
|
if (libyuv::ARGBToYUY2(
|
||||||
|
bgra_buffer->data(), bgra_stride, buffer->data(), out_stride, out_width, out_height) != 0)
|
||||||
|
{
|
||||||
|
LOG_ERROR("Failed to convert BGRA image to YUY2 format.", 0);
|
||||||
|
result = K4A_RESULT_FAILED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG_ERROR("Unsupported image format conversion: %d to %d", in_block->reader->format, target_format);
|
||||||
|
result = K4A_RESULT_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bgra_buffer != NULL)
|
||||||
|
{
|
||||||
|
delete bgra_buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG_ERROR("Unknown target image format: %d", target_format);
|
||||||
|
result = K4A_RESULT_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (K4A_SUCCEEDED(result) && buffer != NULL)
|
||||||
|
{
|
||||||
|
result = TRACE_CALL(k4a_image_create_from_buffer(target_format,
|
||||||
|
out_width,
|
||||||
|
out_height,
|
||||||
|
out_stride,
|
||||||
|
buffer->data(),
|
||||||
|
buffer->size(),
|
||||||
|
&free_vector_buffer,
|
||||||
|
buffer,
|
||||||
|
image_out));
|
||||||
|
k4a_image_set_timestamp_usec(*image_out, in_block->timestamp_ns / 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (K4A_FAILED(result) && buffer != NULL)
|
||||||
|
{
|
||||||
|
delete buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
k4a_result_t new_capture(k4a_playback_context_t *context, block_info_t *block, k4a_capture_t *capture_handle)
|
k4a_result_t new_capture(k4a_playback_context_t *context, block_info_t *block, k4a_capture_t *capture_handle)
|
||||||
{
|
{
|
||||||
RETURN_VALUE_IF_ARG(K4A_RESULT_FAILED, context == NULL);
|
RETURN_VALUE_IF_ARG(K4A_RESULT_FAILED, context == NULL);
|
||||||
|
@ -1525,74 +1768,27 @@ k4a_result_t new_capture(k4a_playback_context_t *context, block_info_t *block, k
|
||||||
RETURN_VALUE_IF_ARG(K4A_RESULT_FAILED, block == nullptr);
|
RETURN_VALUE_IF_ARG(K4A_RESULT_FAILED, block == nullptr);
|
||||||
RETURN_VALUE_IF_ARG(K4A_RESULT_FAILED, block->reader == NULL);
|
RETURN_VALUE_IF_ARG(K4A_RESULT_FAILED, block->reader == NULL);
|
||||||
RETURN_VALUE_IF_ARG(K4A_RESULT_FAILED, block->block == NULL);
|
RETURN_VALUE_IF_ARG(K4A_RESULT_FAILED, block->block == NULL);
|
||||||
RETURN_VALUE_IF_ARG(K4A_RESULT_FAILED, block->block->NumberFrames() != 1);
|
|
||||||
|
|
||||||
if (*capture_handle == 0)
|
if (*capture_handle == 0)
|
||||||
{
|
{
|
||||||
RETURN_IF_ERROR(k4a_capture_create(capture_handle));
|
RETURN_IF_ERROR(k4a_capture_create(capture_handle));
|
||||||
}
|
}
|
||||||
|
|
||||||
DataBuffer &data_buffer = block->block->GetBuffer(0);
|
|
||||||
std::vector<uint8_t> *buffer = new std::vector<uint8_t>(data_buffer.Buffer(),
|
|
||||||
data_buffer.Buffer() + data_buffer.Size());
|
|
||||||
|
|
||||||
if (block->reader->format == K4A_IMAGE_FORMAT_DEPTH16 || block->reader->format == K4A_IMAGE_FORMAT_IR16)
|
|
||||||
{
|
|
||||||
// 16 bit grayscale needs to be converted from big-endian back to little-endian.
|
|
||||||
assert(buffer->size() % sizeof(uint16_t) == 0);
|
|
||||||
uint16_t *buffer_raw = reinterpret_cast<uint16_t *>(buffer->data());
|
|
||||||
size_t buffer_size = buffer->size() / sizeof(uint16_t);
|
|
||||||
for (size_t i = 0; i < buffer_size; i++)
|
|
||||||
{
|
|
||||||
buffer_raw[i] = swap_bytes_16(buffer_raw[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(block->reader->width <= INT_MAX);
|
|
||||||
assert(block->reader->height <= INT_MAX);
|
|
||||||
assert(block->reader->stride <= INT_MAX);
|
|
||||||
k4a_image_t image_handle = NULL;
|
k4a_image_t image_handle = NULL;
|
||||||
k4a_result_t result = K4A_RESULT_SUCCEEDED;
|
k4a_result_t result = K4A_RESULT_SUCCEEDED;
|
||||||
if (block->reader == &context->color_track)
|
if (block->reader == &context->color_track)
|
||||||
{
|
{
|
||||||
result = TRACE_CALL(k4a_image_create_from_buffer(context->record_config.color_format,
|
result = TRACE_CALL(convert_block_to_image(context, block, &image_handle, context->color_format_conversion));
|
||||||
(int)block->reader->width,
|
|
||||||
(int)block->reader->height,
|
|
||||||
(int)block->reader->stride,
|
|
||||||
buffer->data(),
|
|
||||||
buffer->size(),
|
|
||||||
&free_vector_buffer,
|
|
||||||
buffer,
|
|
||||||
&image_handle));
|
|
||||||
k4a_image_set_timestamp_usec(image_handle, block->timestamp_ns / 1000);
|
|
||||||
k4a_capture_set_color_image(*capture_handle, image_handle);
|
k4a_capture_set_color_image(*capture_handle, image_handle);
|
||||||
}
|
}
|
||||||
else if (block->reader == &context->depth_track)
|
else if (block->reader == &context->depth_track)
|
||||||
{
|
{
|
||||||
result = TRACE_CALL(k4a_image_create_from_buffer(K4A_IMAGE_FORMAT_DEPTH16,
|
result = TRACE_CALL(convert_block_to_image(context, block, &image_handle, K4A_IMAGE_FORMAT_DEPTH16));
|
||||||
(int)block->reader->width,
|
|
||||||
(int)block->reader->height,
|
|
||||||
(int)block->reader->stride,
|
|
||||||
buffer->data(),
|
|
||||||
buffer->size(),
|
|
||||||
&free_vector_buffer,
|
|
||||||
buffer,
|
|
||||||
&image_handle));
|
|
||||||
k4a_image_set_timestamp_usec(image_handle, block->timestamp_ns / 1000);
|
|
||||||
k4a_capture_set_depth_image(*capture_handle, image_handle);
|
k4a_capture_set_depth_image(*capture_handle, image_handle);
|
||||||
}
|
}
|
||||||
else if (block->reader == &context->ir_track)
|
else if (block->reader == &context->ir_track)
|
||||||
{
|
{
|
||||||
result = TRACE_CALL(k4a_image_create_from_buffer(K4A_IMAGE_FORMAT_IR16,
|
result = TRACE_CALL(convert_block_to_image(context, block, &image_handle, K4A_IMAGE_FORMAT_IR16));
|
||||||
(int)block->reader->width,
|
|
||||||
(int)block->reader->height,
|
|
||||||
(int)block->reader->stride,
|
|
||||||
buffer->data(),
|
|
||||||
buffer->size(),
|
|
||||||
&free_vector_buffer,
|
|
||||||
buffer,
|
|
||||||
&image_handle));
|
|
||||||
k4a_image_set_timestamp_usec(image_handle, block->timestamp_ns / 1000);
|
|
||||||
k4a_capture_set_ir_image(*capture_handle, image_handle);
|
k4a_capture_set_ir_image(*capture_handle, image_handle);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1605,10 +1801,6 @@ k4a_result_t new_capture(k4a_playback_context_t *context, block_info_t *block, k
|
||||||
{
|
{
|
||||||
k4a_image_release(image_handle);
|
k4a_image_release(image_handle);
|
||||||
}
|
}
|
||||||
if (K4A_FAILED(result))
|
|
||||||
{
|
|
||||||
delete buffer;
|
|
||||||
}
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1634,7 +1826,7 @@ k4a_stream_result_t get_capture(k4a_playback_context_t *context, k4a_capture_t *
|
||||||
{
|
{
|
||||||
enabled_tracks++;
|
enabled_tracks++;
|
||||||
|
|
||||||
// Only read from disk if we haven't aready found the next block for this track
|
// If the current block is NULL, find the next block before/after the seek timestamp.
|
||||||
if (next_blocks[i] == nullptr)
|
if (next_blocks[i] == nullptr)
|
||||||
{
|
{
|
||||||
next_blocks[i] = find_block(context, blocks[i], context->seek_timestamp_ns);
|
next_blocks[i] = find_block(context, blocks[i], context->seek_timestamp_ns);
|
||||||
|
@ -1715,7 +1907,7 @@ k4a_stream_result_t get_capture(k4a_playback_context_t *context, k4a_capture_t *
|
||||||
bool filled = false;
|
bool filled = false;
|
||||||
for (size_t i = 0; i < arraysize(blocks); i++)
|
for (size_t i = 0; i < arraysize(blocks); i++)
|
||||||
{
|
{
|
||||||
if (next_blocks[i] == nullptr && blocks[i]->current_block == nullptr)
|
if (blocks[i]->track != NULL && next_blocks[i] == nullptr && blocks[i]->current_block == nullptr)
|
||||||
{
|
{
|
||||||
std::shared_ptr<block_info_t> test_block = find_block(context,
|
std::shared_ptr<block_info_t> test_block = find_block(context,
|
||||||
blocks[i],
|
blocks[i],
|
||||||
|
@ -1890,6 +2082,7 @@ k4a_stream_result_t get_imu_sample(k4a_playback_context_t *context, k4a_imu_samp
|
||||||
{
|
{
|
||||||
imu_sample->acc_timestamp_usec = sample->acc_timestamp_ns / 1000;
|
imu_sample->acc_timestamp_usec = sample->acc_timestamp_ns / 1000;
|
||||||
imu_sample->gyro_timestamp_usec = sample->gyro_timestamp_ns / 1000;
|
imu_sample->gyro_timestamp_usec = sample->gyro_timestamp_ns / 1000;
|
||||||
|
imu_sample->temperature = std::numeric_limits<float>::quiet_NaN();
|
||||||
for (size_t i = 0; i < 3; i++)
|
for (size_t i = 0; i < 3; i++)
|
||||||
{
|
{
|
||||||
imu_sample->acc_sample.v[i] = sample->acc_data[i];
|
imu_sample->acc_sample.v[i] = sample->acc_data[i];
|
||||||
|
|
|
@ -263,11 +263,11 @@ k4a_result_t write_cluster(k4a_record_context_t *context, cluster_t *cluster, ui
|
||||||
RETURN_VALUE_IF_ARG(K4A_RESULT_FAILED, !context->header_written);
|
RETURN_VALUE_IF_ARG(K4A_RESULT_FAILED, !context->header_written);
|
||||||
RETURN_VALUE_IF_ARG(K4A_RESULT_FAILED, cluster == NULL);
|
RETURN_VALUE_IF_ARG(K4A_RESULT_FAILED, cluster == NULL);
|
||||||
|
|
||||||
k4a_result_t result = K4A_RESULT_SUCCEEDED;
|
|
||||||
if (cluster->data.size() == 0)
|
if (cluster->data.size() == 0)
|
||||||
{
|
{
|
||||||
LOG_WARNING("Tried to write empty cluster to disk", 0);
|
LOG_WARNING("Tried to write empty cluster to disk", 0);
|
||||||
return result;
|
delete cluster;
|
||||||
|
return K4A_RESULT_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort the data in the cluster by timestamp so it can be written in order
|
// Sort the data in the cluster by timestamp so it can be written in order
|
||||||
|
@ -307,6 +307,8 @@ k4a_result_t write_cluster(k4a_record_context_t *context, cluster_t *cluster, ui
|
||||||
KaxTrackEntry *current_track = NULL;
|
KaxTrackEntry *current_track = NULL;
|
||||||
uint64_t block_blob_start = 0;
|
uint64_t block_blob_start = 0;
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<KaxBlockBlob>> blob_list;
|
||||||
|
|
||||||
bool first = true;
|
bool first = true;
|
||||||
for (std::pair<uint64_t, track_data_t> data : cluster->data)
|
for (std::pair<uint64_t, track_data_t> data : cluster->data)
|
||||||
{
|
{
|
||||||
|
@ -317,7 +319,10 @@ k4a_result_t write_cluster(k4a_record_context_t *context, cluster_t *cluster, ui
|
||||||
// We need to decide the block type ahead of time to force IMU into a BlockGroup
|
// We need to decide the block type ahead of time to force IMU into a BlockGroup
|
||||||
block_blob = new KaxBlockBlob(data.second.track == context->imu_track ? BLOCK_BLOB_NO_SIMPLE :
|
block_blob = new KaxBlockBlob(data.second.track == context->imu_track ? BLOCK_BLOB_NO_SIMPLE :
|
||||||
BLOCK_BLOB_ALWAYS_SIMPLE);
|
BLOCK_BLOB_ALWAYS_SIMPLE);
|
||||||
new_cluster->AddBlockBlob(block_blob); // Blob will be freed by libmatroska when the file is closed.
|
// BlockBlob needs to be valid until the cluster is rendered.
|
||||||
|
// The blob will be freed at the end of write_cluster().
|
||||||
|
blob_list.emplace_back(block_blob);
|
||||||
|
new_cluster->AddBlockBlob(block_blob);
|
||||||
block_blob->SetParent(*new_cluster);
|
block_blob->SetParent(*new_cluster);
|
||||||
block_blob_start = data.first;
|
block_blob_start = data.first;
|
||||||
|
|
||||||
|
@ -358,6 +363,8 @@ k4a_result_t write_cluster(k4a_record_context_t *context, cluster_t *cluster, ui
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
k4a_result_t result = K4A_RESULT_SUCCEEDED;
|
||||||
|
|
||||||
auto &cues = GetChild<KaxCues>(*context->file_segment);
|
auto &cues = GetChild<KaxCues>(*context->file_segment);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -381,8 +388,23 @@ k4a_result_t write_cluster(k4a_record_context_t *context, cluster_t *cluster, ui
|
||||||
data.second.buffer->FreeBuffer(*data.second.buffer);
|
data.second.buffer->FreeBuffer(*data.second.buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
delete cluster;
|
// Both KaxCluster and KaxBlockBlob will try to free the same element due to a bug in libmatroska.
|
||||||
|
// In order to prevent this, we need to go through and remove the Block elements from the cluster.
|
||||||
|
auto &elements = new_cluster->GetElementList();
|
||||||
|
for (size_t i = 0; i < elements.size(); i++)
|
||||||
|
{
|
||||||
|
if (EbmlId(*elements[i]) == KaxBlockGroup::ClassInfos.GlobalId ||
|
||||||
|
EbmlId(*elements[i]) == KaxSimpleBlock::ClassInfos.GlobalId)
|
||||||
|
{
|
||||||
|
// Remove this element from the list.
|
||||||
|
// We don't care about preserving order because the Cluster has already been rendered.
|
||||||
|
elements[i] = elements.back();
|
||||||
|
elements.pop_back();
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete cluster;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -225,6 +225,44 @@ k4a_playback_get_tag(k4a_playback_t playback_handle, const char *name, char *val
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
k4a_result_t k4a_playback_set_color_conversion(k4a_playback_t playback_handle, k4a_image_format_t target_format)
|
||||||
|
{
|
||||||
|
RETURN_VALUE_IF_HANDLE_INVALID(K4A_RESULT_FAILED, k4a_playback_t, playback_handle);
|
||||||
|
k4a_playback_context_t *context = k4a_playback_t_get_context(playback_handle);
|
||||||
|
RETURN_VALUE_IF_ARG(K4A_RESULT_FAILED, context == NULL);
|
||||||
|
|
||||||
|
if (context->color_track.track == NULL)
|
||||||
|
{
|
||||||
|
LOG_ERROR("The color track is not enabled in this recording. The color conversion format cannot be set.", 0);
|
||||||
|
return K4A_RESULT_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (target_format)
|
||||||
|
{
|
||||||
|
case K4A_IMAGE_FORMAT_COLOR_MJPG:
|
||||||
|
if (context->color_track.format == K4A_IMAGE_FORMAT_COLOR_MJPG)
|
||||||
|
{
|
||||||
|
context->color_format_conversion = target_format;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG_ERROR("Converting color images to K4A_IMAGE_FORMAT_COLOR_MJPG is not supported.", 0);
|
||||||
|
return K4A_RESULT_FAILED;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case K4A_IMAGE_FORMAT_COLOR_NV12:
|
||||||
|
case K4A_IMAGE_FORMAT_COLOR_YUY2:
|
||||||
|
case K4A_IMAGE_FORMAT_COLOR_BGRA32:
|
||||||
|
context->color_format_conversion = target_format;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG_ERROR("Unsupported target_format specified for format conversion: %d", target_format);
|
||||||
|
return K4A_RESULT_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return K4A_RESULT_SUCCEEDED;
|
||||||
|
}
|
||||||
|
|
||||||
k4a_stream_result_t k4a_playback_get_next_capture(k4a_playback_t playback_handle, k4a_capture_t *capture_handle)
|
k4a_stream_result_t k4a_playback_get_next_capture(k4a_playback_t playback_handle, k4a_capture_t *capture_handle)
|
||||||
{
|
{
|
||||||
RETURN_VALUE_IF_HANDLE_INVALID(K4A_STREAM_RESULT_FAILED, k4a_playback_t, playback_handle);
|
RETURN_VALUE_IF_HANDLE_INVALID(K4A_STREAM_RESULT_FAILED, k4a_playback_t, playback_handle);
|
||||||
|
|
|
@ -168,6 +168,9 @@ k4a_result_t k4a_record_create(const char *path,
|
||||||
set_track_info_video(context->color_track, color_width, color_height, context->camera_fps);
|
set_track_info_video(context->color_track, color_width, color_height, context->camera_fps);
|
||||||
|
|
||||||
uint64_t track_uid = GetChild<KaxTrackUID>(*context->color_track).GetValue();
|
uint64_t track_uid = GetChild<KaxTrackUID>(*context->color_track).GetValue();
|
||||||
|
std::ostringstream track_uid_str;
|
||||||
|
track_uid_str << track_uid;
|
||||||
|
add_tag(context, "K4A_COLOR_TRACK", track_uid_str.str().c_str(), TAG_TARGET_TYPE_TRACK, track_uid);
|
||||||
add_tag(context, "K4A_COLOR_MODE", color_mode_str.str().c_str(), TAG_TARGET_TYPE_TRACK, track_uid);
|
add_tag(context, "K4A_COLOR_MODE", color_mode_str.str().c_str(), TAG_TARGET_TYPE_TRACK, track_uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,6 +196,9 @@ k4a_result_t k4a_record_create(const char *path,
|
||||||
set_track_info_video(context->depth_track, depth_width, depth_height, context->camera_fps);
|
set_track_info_video(context->depth_track, depth_width, depth_height, context->camera_fps);
|
||||||
|
|
||||||
uint64_t track_uid = GetChild<KaxTrackUID>(*context->depth_track).GetValue();
|
uint64_t track_uid = GetChild<KaxTrackUID>(*context->depth_track).GetValue();
|
||||||
|
std::ostringstream track_uid_str;
|
||||||
|
track_uid_str << track_uid;
|
||||||
|
add_tag(context, "K4A_DEPTH_TRACK", track_uid_str.str().c_str(), TAG_TARGET_TYPE_TRACK, track_uid);
|
||||||
add_tag(context, "K4A_DEPTH_MODE", depth_mode_str, TAG_TARGET_TYPE_TRACK, track_uid);
|
add_tag(context, "K4A_DEPTH_MODE", depth_mode_str, TAG_TARGET_TYPE_TRACK, track_uid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -212,6 +218,9 @@ k4a_result_t k4a_record_create(const char *path,
|
||||||
set_track_info_video(context->ir_track, depth_width, depth_height, context->camera_fps);
|
set_track_info_video(context->ir_track, depth_width, depth_height, context->camera_fps);
|
||||||
|
|
||||||
uint64_t track_uid = GetChild<KaxTrackUID>(*context->ir_track).GetValue();
|
uint64_t track_uid = GetChild<KaxTrackUID>(*context->ir_track).GetValue();
|
||||||
|
std::ostringstream track_uid_str;
|
||||||
|
track_uid_str << track_uid;
|
||||||
|
add_tag(context, "K4A_IR_TRACK", track_uid_str.str().c_str(), TAG_TARGET_TYPE_TRACK, track_uid);
|
||||||
add_tag(context,
|
add_tag(context,
|
||||||
"K4A_IR_MODE",
|
"K4A_IR_MODE",
|
||||||
device_config.depth_mode == K4A_DEPTH_MODE_PASSIVE_IR ? "PASSIVE" : "ACTIVE",
|
device_config.depth_mode == K4A_DEPTH_MODE_PASSIVE_IR ? "PASSIVE" : "ACTIVE",
|
||||||
|
@ -383,6 +392,9 @@ k4a_result_t k4a_record_add_imu_track(const k4a_record_t recording_handle)
|
||||||
context->imu_track = add_track(context, "IMU", track_subtitle, "S_K4A/IMU");
|
context->imu_track = add_track(context, "IMU", track_subtitle, "S_K4A/IMU");
|
||||||
|
|
||||||
uint64_t track_uid = GetChild<KaxTrackUID>(*context->imu_track).GetValue();
|
uint64_t track_uid = GetChild<KaxTrackUID>(*context->imu_track).GetValue();
|
||||||
|
std::ostringstream track_uid_str;
|
||||||
|
track_uid_str << track_uid;
|
||||||
|
add_tag(context, "K4A_IMU_TRACK", track_uid_str.str().c_str(), TAG_TARGET_TYPE_TRACK, track_uid);
|
||||||
add_tag(context, "K4A_IMU_MODE", "ON", TAG_TARGET_TYPE_TRACK, track_uid);
|
add_tag(context, "K4A_IMU_MODE", "ON", TAG_TARGET_TYPE_TRACK, track_uid);
|
||||||
|
|
||||||
return K4A_RESULT_SUCCEEDED;
|
return K4A_RESULT_SUCCEEDED;
|
||||||
|
@ -643,6 +655,11 @@ k4a_result_t k4a_record_flush(const k4a_record_t recording_handle)
|
||||||
|
|
||||||
{ // Update seek info
|
{ // Update seek info
|
||||||
auto &seek_head = GetChild<KaxSeekHead>(*context->file_segment);
|
auto &seek_head = GetChild<KaxSeekHead>(*context->file_segment);
|
||||||
|
// RemoveAll() has a bug and does not free the elements before emptying the list.
|
||||||
|
for (auto element : seek_head.GetElementList())
|
||||||
|
{
|
||||||
|
delete element;
|
||||||
|
}
|
||||||
seek_head.RemoveAll(); // Remove any seek entries from previous flushes
|
seek_head.RemoveAll(); // Remove any seek entries from previous flushes
|
||||||
|
|
||||||
seek_head.IndexThis(segment_info, *context->file_segment);
|
seek_head.IndexThis(segment_info, *context->file_segment);
|
||||||
|
|
|
@ -62,6 +62,13 @@ uint32_t k4a_device_get_installed_count(void)
|
||||||
return device_count;
|
return device_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
k4a_result_t k4a_set_debug_message_handler(k4a_logging_message_cb_t *message_cb,
|
||||||
|
void *message_cb_context,
|
||||||
|
k4a_log_level_t min_level)
|
||||||
|
{
|
||||||
|
return logger_register_message_callback(message_cb, message_cb_context, min_level);
|
||||||
|
}
|
||||||
|
|
||||||
depth_cb_streaming_capture_t depth_capture_ready;
|
depth_cb_streaming_capture_t depth_capture_ready;
|
||||||
color_cb_streaming_capture_t color_capture_ready;
|
color_cb_streaming_capture_t color_capture_ready;
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ typedef struct _tewrapper_context_t
|
||||||
COND_HANDLE main_condition;
|
COND_HANDLE main_condition;
|
||||||
LOCK_HANDLE worker_lock;
|
LOCK_HANDLE worker_lock;
|
||||||
COND_HANDLE worker_condition;
|
COND_HANDLE worker_condition;
|
||||||
|
volatile bool thread_started;
|
||||||
volatile bool thread_stop;
|
volatile bool thread_stop;
|
||||||
k4a_result_t thread_start_result;
|
k4a_result_t thread_start_result;
|
||||||
k4a_result_t thread_processing_result;
|
k4a_result_t thread_processing_result;
|
||||||
|
@ -78,6 +79,7 @@ static int transform_engine_thread(void *param)
|
||||||
// The Start routine is blocked waiting for this thread to complete startup, so we signal it here and share our
|
// The Start routine is blocked waiting for this thread to complete startup, so we signal it here and share our
|
||||||
// startup status.
|
// startup status.
|
||||||
Lock(tewrapper->main_lock);
|
Lock(tewrapper->main_lock);
|
||||||
|
tewrapper->thread_started = true;
|
||||||
tewrapper->thread_start_result = result;
|
tewrapper->thread_start_result = result;
|
||||||
Condition_Post(tewrapper->main_condition);
|
Condition_Post(tewrapper->main_condition);
|
||||||
Unlock(tewrapper->main_lock);
|
Unlock(tewrapper->main_lock);
|
||||||
|
@ -212,6 +214,7 @@ tewrapper_t tewrapper_create(k4a_transform_engine_calibration_t *transform_engin
|
||||||
{
|
{
|
||||||
bool locked = false;
|
bool locked = false;
|
||||||
tewrapper->thread_stop = false;
|
tewrapper->thread_stop = false;
|
||||||
|
tewrapper->thread_started = false;
|
||||||
|
|
||||||
THREADAPI_RESULT tresult = ThreadAPI_Create(&tewrapper->thread, transform_engine_thread, tewrapper);
|
THREADAPI_RESULT tresult = ThreadAPI_Create(&tewrapper->thread, transform_engine_thread, tewrapper);
|
||||||
result = K4A_RESULT_FROM_BOOL(tresult == THREADAPI_OK);
|
result = K4A_RESULT_FROM_BOOL(tresult == THREADAPI_OK);
|
||||||
|
@ -220,9 +223,14 @@ tewrapper_t tewrapper_create(k4a_transform_engine_calibration_t *transform_engin
|
||||||
{
|
{
|
||||||
Lock(tewrapper->main_lock);
|
Lock(tewrapper->main_lock);
|
||||||
locked = true;
|
locked = true;
|
||||||
int infinite_timeout = 0;
|
if (!tewrapper->thread_started)
|
||||||
COND_RESULT cond_result = Condition_Wait(tewrapper->main_condition, tewrapper->main_lock, infinite_timeout);
|
{
|
||||||
result = K4A_RESULT_FROM_BOOL(cond_result == COND_OK);
|
int infinite_timeout = 0;
|
||||||
|
COND_RESULT cond_result = Condition_Wait(tewrapper->main_condition,
|
||||||
|
tewrapper->main_lock,
|
||||||
|
infinite_timeout);
|
||||||
|
result = K4A_RESULT_FROM_BOOL(cond_result == COND_OK);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (K4A_SUCCEEDED(result) && K4A_FAILED(tewrapper->thread_start_result))
|
if (K4A_SUCCEEDED(result) && K4A_FAILED(tewrapper->thread_start_result))
|
||||||
|
|
|
@ -66,7 +66,7 @@ static bool transformation_compare_image_descriptors(const k4a_transformation_im
|
||||||
descriptor1->height_pixels != descriptor2->height_pixels ||
|
descriptor1->height_pixels != descriptor2->height_pixels ||
|
||||||
descriptor1->stride_bytes != descriptor2->stride_bytes)
|
descriptor1->stride_bytes != descriptor2->stride_bytes)
|
||||||
{
|
{
|
||||||
LOG_ERROR("Unexpected image descriptor. Expect width_pixels: %d, height_pixels: %d, stride_bytes: %d. "
|
LOG_ERROR("Unexpected image descriptor. Expected width_pixels: %d, height_pixels: %d, stride_bytes: %d. "
|
||||||
"Actual width_pixels: %d, height_pixels: %d, stride_bytes: %d.",
|
"Actual width_pixels: %d, height_pixels: %d, stride_bytes: %d.",
|
||||||
descriptor1->width_pixels,
|
descriptor1->width_pixels,
|
||||||
descriptor1->height_pixels,
|
descriptor1->height_pixels,
|
||||||
|
@ -104,7 +104,7 @@ static k4a_result_t transformation_compute_correspondence(const int depth_index,
|
||||||
const k4a_transformation_rgbz_context_t *context,
|
const k4a_transformation_rgbz_context_t *context,
|
||||||
k4a_correspondence_t *correspondence)
|
k4a_correspondence_t *correspondence)
|
||||||
{
|
{
|
||||||
if (depth == 0)
|
if (depth == 0 || isnan(context->xy_tables->x_table[depth_index]))
|
||||||
{
|
{
|
||||||
memset(correspondence, 0, sizeof(k4a_correspondence_t));
|
memset(correspondence, 0, sizeof(k4a_correspondence_t));
|
||||||
return K4A_RESULT_SUCCEEDED;
|
return K4A_RESULT_SUCCEEDED;
|
||||||
|
@ -459,7 +459,7 @@ static k4a_result_t transformation_depth_to_color(k4a_transformation_rgbz_contex
|
||||||
return K4A_RESULT_SUCCEEDED;
|
return K4A_RESULT_SUCCEEDED;
|
||||||
}
|
}
|
||||||
|
|
||||||
k4a_buffer_result_t transformation_depth_image_to_color_camera_internal(
|
k4a_buffer_result_t transformation_depth_image_to_color_camera_validate_parameters(
|
||||||
const k4a_calibration_t *calibration,
|
const k4a_calibration_t *calibration,
|
||||||
const k4a_transformation_xy_tables_t *xy_tables_depth_camera,
|
const k4a_transformation_xy_tables_t *xy_tables_depth_camera,
|
||||||
const uint8_t *depth_image_data,
|
const uint8_t *depth_image_data,
|
||||||
|
@ -486,10 +486,6 @@ k4a_buffer_result_t transformation_depth_image_to_color_camera_internal(
|
||||||
transformation_compare_image_descriptors(transformed_depth_image_descriptor,
|
transformation_compare_image_descriptors(transformed_depth_image_descriptor,
|
||||||
&expected_transformed_depth_image_descriptor) == false)
|
&expected_transformed_depth_image_descriptor) == false)
|
||||||
{
|
{
|
||||||
memcpy(transformed_depth_image_descriptor,
|
|
||||||
&expected_transformed_depth_image_descriptor,
|
|
||||||
sizeof(k4a_transformation_image_descriptor_t));
|
|
||||||
|
|
||||||
if (transformed_depth_image_data == 0)
|
if (transformed_depth_image_data == 0)
|
||||||
{
|
{
|
||||||
LOG_ERROR("Transformed depth image data is null.", 0);
|
LOG_ERROR("Transformed depth image data is null.", 0);
|
||||||
|
@ -531,6 +527,29 @@ k4a_buffer_result_t transformation_depth_image_to_color_camera_internal(
|
||||||
return K4A_BUFFER_RESULT_FAILED;
|
return K4A_BUFFER_RESULT_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return K4A_BUFFER_RESULT_SUCCEEDED;
|
||||||
|
}
|
||||||
|
|
||||||
|
k4a_buffer_result_t transformation_depth_image_to_color_camera_internal(
|
||||||
|
const k4a_calibration_t *calibration,
|
||||||
|
const k4a_transformation_xy_tables_t *xy_tables_depth_camera,
|
||||||
|
const uint8_t *depth_image_data,
|
||||||
|
const k4a_transformation_image_descriptor_t *depth_image_descriptor,
|
||||||
|
uint8_t *transformed_depth_image_data,
|
||||||
|
k4a_transformation_image_descriptor_t *transformed_depth_image_descriptor)
|
||||||
|
{
|
||||||
|
if (K4A_BUFFER_RESULT_SUCCEEDED !=
|
||||||
|
TRACE_BUFFER_CALL(
|
||||||
|
transformation_depth_image_to_color_camera_validate_parameters(calibration,
|
||||||
|
xy_tables_depth_camera,
|
||||||
|
depth_image_data,
|
||||||
|
depth_image_descriptor,
|
||||||
|
transformed_depth_image_data,
|
||||||
|
transformed_depth_image_descriptor)))
|
||||||
|
{
|
||||||
|
return K4A_BUFFER_RESULT_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
k4a_transformation_rgbz_context_t context;
|
k4a_transformation_rgbz_context_t context;
|
||||||
memset(&context, 0, sizeof(k4a_transformation_rgbz_context_t));
|
memset(&context, 0, sizeof(k4a_transformation_rgbz_context_t));
|
||||||
|
|
||||||
|
@ -641,7 +660,7 @@ static k4a_result_t transformation_color_to_depth(k4a_transformation_rgbz_contex
|
||||||
return K4A_RESULT_SUCCEEDED;
|
return K4A_RESULT_SUCCEEDED;
|
||||||
}
|
}
|
||||||
|
|
||||||
k4a_buffer_result_t transformation_color_image_to_depth_camera_internal(
|
k4a_buffer_result_t transformation_color_image_to_depth_camera_validate_parameters(
|
||||||
const k4a_calibration_t *calibration,
|
const k4a_calibration_t *calibration,
|
||||||
const k4a_transformation_xy_tables_t *xy_tables_depth_camera,
|
const k4a_transformation_xy_tables_t *xy_tables_depth_camera,
|
||||||
const uint8_t *depth_image_data,
|
const uint8_t *depth_image_data,
|
||||||
|
@ -670,10 +689,6 @@ k4a_buffer_result_t transformation_color_image_to_depth_camera_internal(
|
||||||
transformation_compare_image_descriptors(transformed_color_image_descriptor,
|
transformation_compare_image_descriptors(transformed_color_image_descriptor,
|
||||||
&expected_transformed_color_image_descriptor) == false)
|
&expected_transformed_color_image_descriptor) == false)
|
||||||
{
|
{
|
||||||
memcpy(transformed_color_image_descriptor,
|
|
||||||
&expected_transformed_color_image_descriptor,
|
|
||||||
sizeof(k4a_transformation_image_descriptor_t));
|
|
||||||
|
|
||||||
if (transformed_color_image_data == 0)
|
if (transformed_color_image_data == 0)
|
||||||
{
|
{
|
||||||
LOG_ERROR("Transformed color image data is null.", 0);
|
LOG_ERROR("Transformed color image data is null.", 0);
|
||||||
|
@ -731,6 +746,33 @@ k4a_buffer_result_t transformation_color_image_to_depth_camera_internal(
|
||||||
return K4A_BUFFER_RESULT_FAILED;
|
return K4A_BUFFER_RESULT_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return K4A_BUFFER_RESULT_SUCCEEDED;
|
||||||
|
}
|
||||||
|
|
||||||
|
k4a_buffer_result_t transformation_color_image_to_depth_camera_internal(
|
||||||
|
const k4a_calibration_t *calibration,
|
||||||
|
const k4a_transformation_xy_tables_t *xy_tables_depth_camera,
|
||||||
|
const uint8_t *depth_image_data,
|
||||||
|
const k4a_transformation_image_descriptor_t *depth_image_descriptor,
|
||||||
|
const uint8_t *color_image_data,
|
||||||
|
const k4a_transformation_image_descriptor_t *color_image_descriptor,
|
||||||
|
uint8_t *transformed_color_image_data,
|
||||||
|
k4a_transformation_image_descriptor_t *transformed_color_image_descriptor)
|
||||||
|
{
|
||||||
|
if (K4A_BUFFER_RESULT_SUCCEEDED !=
|
||||||
|
TRACE_BUFFER_CALL(
|
||||||
|
transformation_color_image_to_depth_camera_validate_parameters(calibration,
|
||||||
|
xy_tables_depth_camera,
|
||||||
|
depth_image_data,
|
||||||
|
depth_image_descriptor,
|
||||||
|
color_image_data,
|
||||||
|
color_image_descriptor,
|
||||||
|
transformed_color_image_data,
|
||||||
|
transformed_color_image_descriptor)))
|
||||||
|
{
|
||||||
|
return K4A_BUFFER_RESULT_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
k4a_transformation_rgbz_context_t context;
|
k4a_transformation_rgbz_context_t context;
|
||||||
memset(&context, 0, sizeof(k4a_transformation_rgbz_context_t));
|
memset(&context, 0, sizeof(k4a_transformation_rgbz_context_t));
|
||||||
|
|
||||||
|
@ -758,12 +800,24 @@ static void transformation_depth_to_xyz(k4a_transformation_xy_tables_t *xy_table
|
||||||
{
|
{
|
||||||
const uint16_t *depth_image_data_uint16 = (const uint16_t *)depth_image_data;
|
const uint16_t *depth_image_data_uint16 = (const uint16_t *)depth_image_data;
|
||||||
int16_t *xyz_data_int16 = (int16_t *)xyz_image_data;
|
int16_t *xyz_data_int16 = (int16_t *)xyz_image_data;
|
||||||
|
int16_t x, y, z;
|
||||||
|
|
||||||
for (int i = 0; i < xy_tables->width * xy_tables->height; i++)
|
for (int i = 0; i < xy_tables->width * xy_tables->height; i++)
|
||||||
{
|
{
|
||||||
int16_t z = (int16_t)depth_image_data_uint16[i];
|
float x_tab = xy_tables->x_table[i];
|
||||||
int16_t x = (int16_t)(floorf(xy_tables->x_table[i] * (float)z + 0.5f));
|
|
||||||
int16_t y = (int16_t)(floorf(xy_tables->y_table[i] * (float)z + 0.5f));
|
if (!isnan(x_tab))
|
||||||
|
{
|
||||||
|
z = (int16_t)depth_image_data_uint16[i];
|
||||||
|
x = (int16_t)(floorf(x_tab * (float)z + 0.5f));
|
||||||
|
y = (int16_t)(floorf(xy_tables->y_table[i] * (float)z + 0.5f));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
x = 0;
|
||||||
|
y = 0;
|
||||||
|
z = 0;
|
||||||
|
}
|
||||||
|
|
||||||
xyz_data_int16[3 * i + 0] = x;
|
xyz_data_int16[3 * i + 0] = x;
|
||||||
xyz_data_int16[3 * i + 1] = y;
|
xyz_data_int16[3 * i + 1] = y;
|
||||||
|
@ -796,16 +850,28 @@ static void transformation_depth_to_xyz_sse(k4a_transformation_xy_tables_t *xy_t
|
||||||
// z2, z5, z0, z3, z6, z1, z4, z7
|
// z2, z5, z0, z3, z6, z1, z4, z7
|
||||||
__m128i z_shuffle = _mm_setr_epi16(pos2, pos5, pos0, pos3, pos6, pos1, pos4, pos7);
|
__m128i z_shuffle = _mm_setr_epi16(pos2, pos5, pos0, pos3, pos6, pos1, pos4, pos7);
|
||||||
|
|
||||||
|
__m128i valid_shuffle = _mm_setr_epi16(pos0, pos2, pos4, pos6, pos0, pos2, pos4, pos6);
|
||||||
|
|
||||||
for (int i = 0; i < xy_tables->width * xy_tables->height / 8; i++)
|
for (int i = 0; i < xy_tables->width * xy_tables->height / 8; i++)
|
||||||
{
|
{
|
||||||
__m128i z = *depth_image_data_m128i++;
|
__m128i z = *depth_image_data_m128i++;
|
||||||
|
|
||||||
|
__m128 x_tab_lo = *x_table_m128++;
|
||||||
|
__m128 x_tab_hi = *x_table_m128++;
|
||||||
|
__m128 valid_lo = _mm_cmpeq_ps(x_tab_lo, x_tab_lo);
|
||||||
|
__m128 valid_hi = _mm_cmpeq_ps(x_tab_hi, x_tab_hi);
|
||||||
|
__m128i valid_shuffle_lo = _mm_shuffle_epi8(*((__m128i *)&valid_lo), valid_shuffle);
|
||||||
|
__m128i valid_shuffle_hi = _mm_shuffle_epi8(*((__m128i *)&valid_hi), valid_shuffle);
|
||||||
|
__m128i valid = _mm_blend_epi16(valid_shuffle_lo, valid_shuffle_hi, 0xF0);
|
||||||
|
z = _mm_blendv_epi8(_mm_setzero_si128(), z, valid);
|
||||||
|
|
||||||
__m128 depth_lo = _mm_cvtepi32_ps(_mm_unpacklo_epi16(z, _mm_setzero_si128()));
|
__m128 depth_lo = _mm_cvtepi32_ps(_mm_unpacklo_epi16(z, _mm_setzero_si128()));
|
||||||
__m128 depth_hi = _mm_cvtepi32_ps(_mm_unpackhi_epi16(z, _mm_setzero_si128()));
|
__m128 depth_hi = _mm_cvtepi32_ps(_mm_unpackhi_epi16(z, _mm_setzero_si128()));
|
||||||
|
|
||||||
__m128i x_lo = _mm_cvtps_epi32(_mm_mul_ps(depth_lo, *x_table_m128++));
|
__m128i x_lo = _mm_cvtps_epi32(_mm_mul_ps(depth_lo, x_tab_lo));
|
||||||
__m128i x_hi = _mm_cvtps_epi32(_mm_mul_ps(depth_hi, *x_table_m128++));
|
__m128i x_hi = _mm_cvtps_epi32(_mm_mul_ps(depth_hi, x_tab_hi));
|
||||||
__m128i x = _mm_packs_epi32(x_lo, x_hi);
|
__m128i x = _mm_packs_epi32(x_lo, x_hi);
|
||||||
|
x = _mm_blendv_epi8(_mm_setzero_si128(), x, valid);
|
||||||
x = _mm_shuffle_epi8(x, x_shuffle);
|
x = _mm_shuffle_epi8(x, x_shuffle);
|
||||||
|
|
||||||
__m128i y_lo = _mm_cvtps_epi32(_mm_mul_ps(depth_lo, *y_table_m128++));
|
__m128i y_lo = _mm_cvtps_epi32(_mm_mul_ps(depth_lo, *y_table_m128++));
|
||||||
|
@ -845,8 +911,6 @@ transformation_depth_image_to_point_cloud_internal(k4a_transformation_xy_tables_
|
||||||
if (xyz_image_data == 0 ||
|
if (xyz_image_data == 0 ||
|
||||||
transformation_compare_image_descriptors(xyz_image_descriptor, &expected_xyz_image_descriptor) == false)
|
transformation_compare_image_descriptors(xyz_image_descriptor, &expected_xyz_image_descriptor) == false)
|
||||||
{
|
{
|
||||||
memcpy(xyz_image_descriptor, &expected_xyz_image_descriptor, sizeof(k4a_transformation_image_descriptor_t));
|
|
||||||
|
|
||||||
if (xyz_image_data == 0)
|
if (xyz_image_data == 0)
|
||||||
{
|
{
|
||||||
LOG_ERROR("XYZ image data is null.", 0);
|
LOG_ERROR("XYZ image data is null.", 0);
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
// System dependencies
|
// System dependencies
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
k4a_result_t transformation_get_mode_specific_calibration(const k4a_calibration_camera_t *depth_camera_calibration,
|
k4a_result_t transformation_get_mode_specific_calibration(const k4a_calibration_camera_t *depth_camera_calibration,
|
||||||
const k4a_calibration_camera_t *color_camera_calibration,
|
const k4a_calibration_camera_t *color_camera_calibration,
|
||||||
|
@ -316,7 +317,9 @@ static k4a_buffer_result_t transformation_init_xy_tables(const k4a_calibration_t
|
||||||
|
|
||||||
if (valid == 0)
|
if (valid == 0)
|
||||||
{
|
{
|
||||||
xy_tables->x_table[idx] = 0.f;
|
// x table value of NAN marks invalid
|
||||||
|
xy_tables->x_table[idx] = NAN;
|
||||||
|
// set y table value to 0 to speed up SSE implementation
|
||||||
xy_tables->y_table[idx] = 0.f;
|
xy_tables->y_table[idx] = 0.f;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -467,6 +470,18 @@ transformation_depth_image_to_color_camera(k4a_transformation_t transformation_h
|
||||||
|
|
||||||
if (transformation_context->enable_gpu_optimization)
|
if (transformation_context->enable_gpu_optimization)
|
||||||
{
|
{
|
||||||
|
if (K4A_BUFFER_RESULT_SUCCEEDED !=
|
||||||
|
TRACE_BUFFER_CALL(transformation_depth_image_to_color_camera_validate_parameters(
|
||||||
|
&transformation_context->calibration,
|
||||||
|
&transformation_context->depth_camera_xy_tables,
|
||||||
|
depth_image_data,
|
||||||
|
depth_image_descriptor,
|
||||||
|
transformed_depth_image_data,
|
||||||
|
transformed_depth_image_descriptor)))
|
||||||
|
{
|
||||||
|
return K4A_RESULT_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
size_t depth_image_size = (size_t)(depth_image_descriptor->stride_bytes *
|
size_t depth_image_size = (size_t)(depth_image_descriptor->stride_bytes *
|
||||||
depth_image_descriptor->height_pixels);
|
depth_image_descriptor->height_pixels);
|
||||||
size_t transformed_depth_image_size = (size_t)(transformed_depth_image_descriptor->stride_bytes *
|
size_t transformed_depth_image_size = (size_t)(transformed_depth_image_descriptor->stride_bytes *
|
||||||
|
@ -522,6 +537,20 @@ transformation_color_image_to_depth_camera(k4a_transformation_t transformation_h
|
||||||
|
|
||||||
if (transformation_context->enable_gpu_optimization)
|
if (transformation_context->enable_gpu_optimization)
|
||||||
{
|
{
|
||||||
|
if (K4A_BUFFER_RESULT_SUCCEEDED !=
|
||||||
|
TRACE_BUFFER_CALL(transformation_color_image_to_depth_camera_validate_parameters(
|
||||||
|
&transformation_context->calibration,
|
||||||
|
&transformation_context->depth_camera_xy_tables,
|
||||||
|
depth_image_data,
|
||||||
|
depth_image_descriptor,
|
||||||
|
color_image_data,
|
||||||
|
color_image_descriptor,
|
||||||
|
transformed_color_image_data,
|
||||||
|
transformed_color_image_descriptor)))
|
||||||
|
{
|
||||||
|
return K4A_RESULT_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
size_t depth_image_size = (size_t)(depth_image_descriptor->stride_bytes *
|
size_t depth_image_size = (size_t)(depth_image_descriptor->stride_bytes *
|
||||||
depth_image_descriptor->height_pixels);
|
depth_image_descriptor->height_pixels);
|
||||||
size_t color_image_size = (size_t)(color_image_descriptor->stride_bytes *
|
size_t color_image_size = (size_t)(color_image_descriptor->stride_bytes *
|
||||||
|
|
|
@ -7,13 +7,11 @@ add_library(k4a_usb_cmd STATIC
|
||||||
)
|
)
|
||||||
|
|
||||||
# Consumers should #include <k4ainternal/usbcommand.h>
|
# Consumers should #include <k4ainternal/usbcommand.h>
|
||||||
target_include_directories(k4a_usb_cmd PUBLIC
|
target_include_directories(k4a_usb_cmd PUBLIC
|
||||||
${K4A_PRIV_INCLUDE_DIR})
|
${K4A_PRIV_INCLUDE_DIR})
|
||||||
|
|
||||||
find_package(LibUSB REQUIRED)
|
|
||||||
|
|
||||||
# Dependencies of this library
|
# Dependencies of this library
|
||||||
target_link_libraries(k4a_usb_cmd PUBLIC
|
target_link_libraries(k4a_usb_cmd PUBLIC
|
||||||
azure::aziotsharedutil
|
azure::aziotsharedutil
|
||||||
LibUSB::LibUSB
|
LibUSB::LibUSB
|
||||||
k4ainternal::allocator
|
k4ainternal::allocator
|
||||||
|
|
|
@ -27,8 +27,7 @@ TraceLibUsbError(int err, const char *szCall, const char *szFile, int line, cons
|
||||||
// Example print:
|
// Example print:
|
||||||
// depth.cpp (86): allocator_create(&depth->allocator) returned ERROR_NOT_FOUND in depth_create
|
// depth.cpp (86): allocator_create(&depth->allocator) returned ERROR_NOT_FOUND in depth_create
|
||||||
|
|
||||||
LOG_ERROR(
|
LOG_ERROR("%s (%d): %s returned %s in %s ", szFile, line, szCall, libusb_error_name(err), szFunction);
|
||||||
LOGGER_K4A, "%s (%d): %s returned %s in %s ", szFile, line, szCall, libusb_error_name(err), szFunction);
|
|
||||||
result = K4A_RESULT_FAILED;
|
result = K4A_RESULT_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ add_subdirectory(DepthTests)
|
||||||
add_subdirectory(example)
|
add_subdirectory(example)
|
||||||
add_subdirectory(ExternLibraries)
|
add_subdirectory(ExternLibraries)
|
||||||
add_subdirectory(FirmwareTests)
|
add_subdirectory(FirmwareTests)
|
||||||
|
add_subdirectory(logging)
|
||||||
add_subdirectory(IMUTests)
|
add_subdirectory(IMUTests)
|
||||||
add_subdirectory(multidevice)
|
add_subdirectory(multidevice)
|
||||||
add_subdirectory(projections)
|
add_subdirectory(projections)
|
||||||
|
|
|
@ -18,8 +18,9 @@ protected:
|
||||||
{
|
{
|
||||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED, TRACE_CALL(setup_common_test()));
|
ASSERT_EQ(K4A_RESULT_SUCCEEDED, TRACE_CALL(setup_common_test()));
|
||||||
const ::testing::TestInfo *const test_info = ::testing::UnitTest::GetInstance()->current_test_info();
|
const ::testing::TestInfo *const test_info = ::testing::UnitTest::GetInstance()->current_test_info();
|
||||||
LOG_INFO("Test %s requires a connection exerciser.", test_info->name());
|
printf("\nStarting test %s. This requires a connection exerciser to be connected.\n", test_info->name());
|
||||||
LOG_INFO("Disconnecting the device", 0);
|
LOG_INFO("Starting test %s.", test_info->name());
|
||||||
|
LOG_INFO("Disconnecting the device");
|
||||||
g_connection_exerciser->set_usb_port(0);
|
g_connection_exerciser->set_usb_port(0);
|
||||||
ThreadAPI_Sleep(500);
|
ThreadAPI_Sleep(500);
|
||||||
|
|
||||||
|
@ -40,7 +41,7 @@ protected:
|
||||||
ASSERT_FALSE(compare_version(g_test_firmware_package_info.depth, g_candidate_firmware_package_info.depth));
|
ASSERT_FALSE(compare_version(g_test_firmware_package_info.depth, g_candidate_firmware_package_info.depth));
|
||||||
ASSERT_FALSE(compare_version(g_test_firmware_package_info.rgb, g_candidate_firmware_package_info.rgb));
|
ASSERT_FALSE(compare_version(g_test_firmware_package_info.rgb, g_candidate_firmware_package_info.rgb));
|
||||||
|
|
||||||
// There should be no other devices.
|
// There should be no other devices as the tests use the default device to connect to.
|
||||||
uint32_t device_count = 0;
|
uint32_t device_count = 0;
|
||||||
usb_cmd_get_device_count(&device_count);
|
usb_cmd_get_device_count(&device_count);
|
||||||
ASSERT_EQ((uint32_t)0, device_count);
|
ASSERT_EQ((uint32_t)0, device_count);
|
||||||
|
@ -78,7 +79,8 @@ protected:
|
||||||
|
|
||||||
k4a_result_t connect_device()
|
k4a_result_t connect_device()
|
||||||
{
|
{
|
||||||
k4a_result_t result = g_connection_exerciser->set_usb_port(g_k4a_port_number);
|
k4a_result_t result = TRACE_CALL(g_connection_exerciser->set_usb_port(g_k4a_port_number));
|
||||||
|
ThreadAPI_Sleep(1000);
|
||||||
|
|
||||||
if (K4A_SUCCEEDED(result))
|
if (K4A_SUCCEEDED(result))
|
||||||
{
|
{
|
||||||
|
@ -108,7 +110,7 @@ protected:
|
||||||
firmware_handle = nullptr;
|
firmware_handle = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
k4a_result_t result = g_connection_exerciser->set_usb_port(0);
|
k4a_result_t result = TRACE_CALL(g_connection_exerciser->set_usb_port(0));
|
||||||
if (K4A_SUCCEEDED(result))
|
if (K4A_SUCCEEDED(result))
|
||||||
{
|
{
|
||||||
ThreadAPI_Sleep(1000);
|
ThreadAPI_Sleep(1000);
|
||||||
|
@ -220,7 +222,7 @@ TEST_F(firmware_fw, DISABLED_update_timing)
|
||||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED,
|
ASSERT_EQ(K4A_RESULT_SUCCEEDED,
|
||||||
firmware_get_device_serialnum(firmware_handle, serial_number, &serial_number_length));
|
firmware_get_device_serialnum(firmware_handle, serial_number, &serial_number_length));
|
||||||
|
|
||||||
LOG_INFO("Updating the device to the Candidate firmware.");
|
printf("\n == Updating the device to the Candidate firmware.\n");
|
||||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED,
|
ASSERT_EQ(K4A_RESULT_SUCCEEDED,
|
||||||
perform_device_update(&firmware_handle,
|
perform_device_update(&firmware_handle,
|
||||||
g_candidate_firmware_buffer,
|
g_candidate_firmware_buffer,
|
||||||
|
@ -230,7 +232,7 @@ TEST_F(firmware_fw, DISABLED_update_timing)
|
||||||
|
|
||||||
ASSERT_TRUE(compare_device_serial_number(firmware_handle, serial_number));
|
ASSERT_TRUE(compare_device_serial_number(firmware_handle, serial_number));
|
||||||
|
|
||||||
LOG_INFO("Updating the device to the Test firmware.");
|
printf("\n == Updating the device to the Test firmware.\n");
|
||||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED,
|
ASSERT_EQ(K4A_RESULT_SUCCEEDED,
|
||||||
perform_device_update(&firmware_handle,
|
perform_device_update(&firmware_handle,
|
||||||
g_test_firmware_buffer,
|
g_test_firmware_buffer,
|
||||||
|
@ -245,9 +247,7 @@ TEST_F(firmware_fw, simple_update_from_lkg)
|
||||||
{
|
{
|
||||||
LOG_INFO("Beginning the basic update test from the LKG firmware.", 0);
|
LOG_INFO("Beginning the basic update test from the LKG firmware.", 0);
|
||||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED, connect_device());
|
ASSERT_EQ(K4A_RESULT_SUCCEEDED, connect_device());
|
||||||
|
|
||||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED, read_calibration(&calibration_pre_update, &calibration_pre_update_size));
|
ASSERT_EQ(K4A_RESULT_SUCCEEDED, read_calibration(&calibration_pre_update, &calibration_pre_update_size));
|
||||||
|
|
||||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED, open_firmware_device(&firmware_handle));
|
ASSERT_EQ(K4A_RESULT_SUCCEEDED, open_firmware_device(&firmware_handle));
|
||||||
|
|
||||||
ASSERT_EQ(K4A_BUFFER_RESULT_TOO_SMALL, firmware_get_device_serialnum(firmware_handle, NULL, &serial_number_length));
|
ASSERT_EQ(K4A_BUFFER_RESULT_TOO_SMALL, firmware_get_device_serialnum(firmware_handle, NULL, &serial_number_length));
|
||||||
|
@ -258,7 +258,7 @@ TEST_F(firmware_fw, simple_update_from_lkg)
|
||||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED,
|
ASSERT_EQ(K4A_RESULT_SUCCEEDED,
|
||||||
firmware_get_device_serialnum(firmware_handle, serial_number, &serial_number_length));
|
firmware_get_device_serialnum(firmware_handle, serial_number, &serial_number_length));
|
||||||
|
|
||||||
LOG_INFO("Updating the device to the LKG firmware.");
|
printf("\n == Updating the device to the LKG firmware.\n");
|
||||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED,
|
ASSERT_EQ(K4A_RESULT_SUCCEEDED,
|
||||||
perform_device_update(&firmware_handle,
|
perform_device_update(&firmware_handle,
|
||||||
g_lkg_firmware_buffer,
|
g_lkg_firmware_buffer,
|
||||||
|
@ -275,7 +275,7 @@ TEST_F(firmware_fw, simple_update_from_lkg)
|
||||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED, open_firmware_device(&firmware_handle));
|
ASSERT_EQ(K4A_RESULT_SUCCEEDED, open_firmware_device(&firmware_handle));
|
||||||
ASSERT_TRUE(compare_device_serial_number(firmware_handle, serial_number));
|
ASSERT_TRUE(compare_device_serial_number(firmware_handle, serial_number));
|
||||||
|
|
||||||
LOG_INFO("Updating the device to the Candidate firmware.");
|
printf("\n == Updating the device to the Candidate firmware.\n");
|
||||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED,
|
ASSERT_EQ(K4A_RESULT_SUCCEEDED,
|
||||||
perform_device_update(&firmware_handle,
|
perform_device_update(&firmware_handle,
|
||||||
g_candidate_firmware_buffer,
|
g_candidate_firmware_buffer,
|
||||||
|
@ -292,7 +292,7 @@ TEST_F(firmware_fw, simple_update_from_lkg)
|
||||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED, open_firmware_device(&firmware_handle));
|
ASSERT_EQ(K4A_RESULT_SUCCEEDED, open_firmware_device(&firmware_handle));
|
||||||
ASSERT_TRUE(compare_device_serial_number(firmware_handle, serial_number));
|
ASSERT_TRUE(compare_device_serial_number(firmware_handle, serial_number));
|
||||||
|
|
||||||
LOG_INFO("Updating the device to the Test firmware.");
|
printf("\n == Updating the device to the Test firmware.\n");
|
||||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED,
|
ASSERT_EQ(K4A_RESULT_SUCCEEDED,
|
||||||
perform_device_update(&firmware_handle,
|
perform_device_update(&firmware_handle,
|
||||||
g_test_firmware_buffer,
|
g_test_firmware_buffer,
|
||||||
|
@ -314,9 +314,7 @@ TEST_F(firmware_fw, simple_update_from_factory)
|
||||||
{
|
{
|
||||||
LOG_INFO("Beginning the basic update test from the Factory firmware.", 0);
|
LOG_INFO("Beginning the basic update test from the Factory firmware.", 0);
|
||||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED, connect_device());
|
ASSERT_EQ(K4A_RESULT_SUCCEEDED, connect_device());
|
||||||
|
|
||||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED, read_calibration(&calibration_pre_update, &calibration_pre_update_size));
|
ASSERT_EQ(K4A_RESULT_SUCCEEDED, read_calibration(&calibration_pre_update, &calibration_pre_update_size));
|
||||||
|
|
||||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED, open_firmware_device(&firmware_handle));
|
ASSERT_EQ(K4A_RESULT_SUCCEEDED, open_firmware_device(&firmware_handle));
|
||||||
|
|
||||||
ASSERT_EQ(K4A_BUFFER_RESULT_TOO_SMALL, firmware_get_device_serialnum(firmware_handle, NULL, &serial_number_length));
|
ASSERT_EQ(K4A_BUFFER_RESULT_TOO_SMALL, firmware_get_device_serialnum(firmware_handle, NULL, &serial_number_length));
|
||||||
|
@ -327,7 +325,7 @@ TEST_F(firmware_fw, simple_update_from_factory)
|
||||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED,
|
ASSERT_EQ(K4A_RESULT_SUCCEEDED,
|
||||||
firmware_get_device_serialnum(firmware_handle, serial_number, &serial_number_length));
|
firmware_get_device_serialnum(firmware_handle, serial_number, &serial_number_length));
|
||||||
|
|
||||||
LOG_INFO("Updating the device to the Factory firmware.");
|
printf("\n == Updating the device to the Factory firmware.\n");
|
||||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED,
|
ASSERT_EQ(K4A_RESULT_SUCCEEDED,
|
||||||
perform_device_update(&firmware_handle,
|
perform_device_update(&firmware_handle,
|
||||||
g_factory_firmware_buffer,
|
g_factory_firmware_buffer,
|
||||||
|
@ -344,7 +342,7 @@ TEST_F(firmware_fw, simple_update_from_factory)
|
||||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED, open_firmware_device(&firmware_handle));
|
ASSERT_EQ(K4A_RESULT_SUCCEEDED, open_firmware_device(&firmware_handle));
|
||||||
ASSERT_TRUE(compare_device_serial_number(firmware_handle, serial_number));
|
ASSERT_TRUE(compare_device_serial_number(firmware_handle, serial_number));
|
||||||
|
|
||||||
LOG_INFO("Updating the device to the Candidate firmware.");
|
printf("\n == Updating the device to the Candidate firmware.\n");
|
||||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED,
|
ASSERT_EQ(K4A_RESULT_SUCCEEDED,
|
||||||
perform_device_update(&firmware_handle,
|
perform_device_update(&firmware_handle,
|
||||||
g_candidate_firmware_buffer,
|
g_candidate_firmware_buffer,
|
||||||
|
@ -361,7 +359,7 @@ TEST_F(firmware_fw, simple_update_from_factory)
|
||||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED, open_firmware_device(&firmware_handle));
|
ASSERT_EQ(K4A_RESULT_SUCCEEDED, open_firmware_device(&firmware_handle));
|
||||||
ASSERT_TRUE(compare_device_serial_number(firmware_handle, serial_number));
|
ASSERT_TRUE(compare_device_serial_number(firmware_handle, serial_number));
|
||||||
|
|
||||||
LOG_INFO("Updating the device to the Test firmware.");
|
printf("\n == Updating the device to the Test firmware.\n");
|
||||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED,
|
ASSERT_EQ(K4A_RESULT_SUCCEEDED,
|
||||||
perform_device_update(&firmware_handle,
|
perform_device_update(&firmware_handle,
|
||||||
g_test_firmware_buffer,
|
g_test_firmware_buffer,
|
||||||
|
|
|
@ -47,23 +47,25 @@
|
||||||
return K4A_RESULT_FAILED; \
|
return K4A_RESULT_FAILED; \
|
||||||
}
|
}
|
||||||
|
|
||||||
char *g_candidate_firmware_path = nullptr;
|
|
||||||
|
|
||||||
int g_k4a_port_number = -1;
|
int g_k4a_port_number = -1;
|
||||||
connection_exerciser *g_connection_exerciser = nullptr;
|
connection_exerciser *g_connection_exerciser = nullptr;
|
||||||
|
|
||||||
uint8_t *g_test_firmware_buffer = nullptr;
|
char *g_candidate_firmware_path = nullptr;
|
||||||
size_t g_test_firmware_size = 0;
|
|
||||||
firmware_package_info_t g_test_firmware_package_info = { 0 };
|
|
||||||
|
|
||||||
uint8_t *g_candidate_firmware_buffer = nullptr;
|
uint8_t *g_candidate_firmware_buffer = nullptr;
|
||||||
size_t g_candidate_firmware_size = 0;
|
size_t g_candidate_firmware_size = 0;
|
||||||
firmware_package_info_t g_candidate_firmware_package_info = { 0 };
|
firmware_package_info_t g_candidate_firmware_package_info = { 0 };
|
||||||
|
|
||||||
|
char *g_test_firmware_path = nullptr;
|
||||||
|
uint8_t *g_test_firmware_buffer = nullptr;
|
||||||
|
size_t g_test_firmware_size = 0;
|
||||||
|
firmware_package_info_t g_test_firmware_package_info = { 0 };
|
||||||
|
|
||||||
|
char *g_lkg_firmware_path = nullptr;
|
||||||
uint8_t *g_lkg_firmware_buffer = nullptr;
|
uint8_t *g_lkg_firmware_buffer = nullptr;
|
||||||
size_t g_lkg_firmware_size = 0;
|
size_t g_lkg_firmware_size = 0;
|
||||||
firmware_package_info_t g_lkg_firmware_package_info = { 0 };
|
firmware_package_info_t g_lkg_firmware_package_info = { 0 };
|
||||||
|
|
||||||
|
char *g_factory_firmware_path = nullptr;
|
||||||
uint8_t *g_factory_firmware_buffer = nullptr;
|
uint8_t *g_factory_firmware_buffer = nullptr;
|
||||||
size_t g_factory_firmware_size = 0;
|
size_t g_factory_firmware_size = 0;
|
||||||
firmware_package_info_t g_factory_firmware_package_info = { 0 };
|
firmware_package_info_t g_factory_firmware_package_info = { 0 };
|
||||||
|
@ -90,12 +92,12 @@ k4a_result_t setup_common_test()
|
||||||
g_k4a_port_number = -1;
|
g_k4a_port_number = -1;
|
||||||
g_connection_exerciser = new (std::nothrow) connection_exerciser();
|
g_connection_exerciser = new (std::nothrow) connection_exerciser();
|
||||||
|
|
||||||
LOG_INFO("Searching for Connection Exerciser...", 0);
|
std::cout << "Searching for the connection exerciser and device..." << std::endl;
|
||||||
|
LOG_INFO("Searching for the connection exerciser...", 0);
|
||||||
K4A_TEST_VERIFY_SUCCEEDED(g_connection_exerciser->find_connection_exerciser());
|
K4A_TEST_VERIFY_SUCCEEDED(g_connection_exerciser->find_connection_exerciser());
|
||||||
LOG_INFO("Clearing port...");
|
|
||||||
K4A_TEST_VERIFY_SUCCEEDED(g_connection_exerciser->set_usb_port(0));
|
K4A_TEST_VERIFY_SUCCEEDED(g_connection_exerciser->set_usb_port(0));
|
||||||
|
|
||||||
LOG_INFO("Searching for device...");
|
LOG_INFO("Searching for device...", 0);
|
||||||
|
|
||||||
for (int i = 0; i < CONN_EX_MAX_NUM_PORTS; ++i)
|
for (int i = 0; i < CONN_EX_MAX_NUM_PORTS; ++i)
|
||||||
{
|
{
|
||||||
|
@ -123,30 +125,30 @@ k4a_result_t setup_common_test()
|
||||||
g_k4a_port_number = port;
|
g_k4a_port_number = port;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("On port #%d: %4.2fV %4.2fA\n", port, voltage, current);
|
LOG_INFO("On port #%d: %4.2fV %4.2fA\n", port, voltage, current);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (g_k4a_port_number == -1)
|
if (g_k4a_port_number == -1)
|
||||||
{
|
{
|
||||||
std::cout << "The Kinect for Azure was not detected on a port of the connection exerciser." << std::endl;
|
std::cout << "The Azure Kinect DK was not detected on any port of the connection exerciser." << std::endl;
|
||||||
return K4A_RESULT_FAILED;
|
return K4A_RESULT_FAILED;
|
||||||
}
|
}
|
||||||
K4A_TEST_VERIFY_SUCCEEDED(g_connection_exerciser->set_usb_port(0));
|
K4A_TEST_VERIFY_SUCCEEDED(g_connection_exerciser->set_usb_port(0));
|
||||||
|
|
||||||
std::cout << "Loading Test firmware package: " << K4A_TEST_FIRMWARE_PATH << std::endl;
|
|
||||||
load_firmware_files(K4A_TEST_FIRMWARE_PATH, &g_test_firmware_buffer, &g_test_firmware_size);
|
|
||||||
parse_firmware_package(g_test_firmware_buffer, g_test_firmware_size, &g_test_firmware_package_info);
|
|
||||||
|
|
||||||
std::cout << "Loading Release Candidate firmware package: " << g_candidate_firmware_path << std::endl;
|
std::cout << "Loading Release Candidate firmware package: " << g_candidate_firmware_path << std::endl;
|
||||||
load_firmware_files(g_candidate_firmware_path, &g_candidate_firmware_buffer, &g_candidate_firmware_size);
|
load_firmware_files(g_candidate_firmware_path, &g_candidate_firmware_buffer, &g_candidate_firmware_size);
|
||||||
parse_firmware_package(g_candidate_firmware_buffer, g_candidate_firmware_size, &g_candidate_firmware_package_info);
|
parse_firmware_package(g_candidate_firmware_buffer, g_candidate_firmware_size, &g_candidate_firmware_package_info);
|
||||||
|
|
||||||
std::cout << "Loading LKG firmware package: " << K4A_LKG_FIRMWARE_PATH << std::endl;
|
std::cout << "Loading Test firmware package: " << g_test_firmware_path << std::endl;
|
||||||
load_firmware_files(K4A_LKG_FIRMWARE_PATH, &g_lkg_firmware_buffer, &g_lkg_firmware_size);
|
load_firmware_files(g_test_firmware_path, &g_test_firmware_buffer, &g_test_firmware_size);
|
||||||
|
parse_firmware_package(g_test_firmware_buffer, g_test_firmware_size, &g_test_firmware_package_info);
|
||||||
|
|
||||||
|
std::cout << "Loading LKG firmware package: " << g_lkg_firmware_path << std::endl;
|
||||||
|
load_firmware_files(g_lkg_firmware_path, &g_lkg_firmware_buffer, &g_lkg_firmware_size);
|
||||||
parse_firmware_package(g_lkg_firmware_buffer, g_lkg_firmware_size, &g_lkg_firmware_package_info);
|
parse_firmware_package(g_lkg_firmware_buffer, g_lkg_firmware_size, &g_lkg_firmware_package_info);
|
||||||
|
|
||||||
std::cout << "Loading Factory firmware package: " << K4A_FACTORY_FIRMWARE_PATH << std::endl;
|
std::cout << "Loading Factory firmware package: " << g_factory_firmware_path << std::endl;
|
||||||
load_firmware_files(K4A_FACTORY_FIRMWARE_PATH, &g_factory_firmware_buffer, &g_factory_firmware_size);
|
load_firmware_files(g_factory_firmware_path, &g_factory_firmware_buffer, &g_factory_firmware_size);
|
||||||
parse_firmware_package(g_factory_firmware_buffer, g_factory_firmware_size, &g_factory_firmware_package_info);
|
parse_firmware_package(g_factory_firmware_buffer, g_factory_firmware_size, &g_factory_firmware_package_info);
|
||||||
|
|
||||||
common_initialized = true;
|
common_initialized = true;
|
||||||
|
@ -206,7 +208,7 @@ k4a_result_t load_firmware_files(char *firmware_path, uint8_t **firmware_buffer,
|
||||||
size_t tempFirmwareSize = (size_t)ftell(pFirmwareFile);
|
size_t tempFirmwareSize = (size_t)ftell(pFirmwareFile);
|
||||||
if (tempFirmwareSize == (size_t)-1L)
|
if (tempFirmwareSize == (size_t)-1L)
|
||||||
{
|
{
|
||||||
std::cout << "ERROR: Failed to get size of the firmware package." << std::endl;
|
LOG_ERROR("Failed to get size of the firmware package.", 0);
|
||||||
return K4A_RESULT_FAILED;
|
return K4A_RESULT_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,17 +217,17 @@ k4a_result_t load_firmware_files(char *firmware_path, uint8_t **firmware_buffer,
|
||||||
tempFirmwareBuffer = (uint8_t *)malloc(tempFirmwareSize);
|
tempFirmwareBuffer = (uint8_t *)malloc(tempFirmwareSize);
|
||||||
if (tempFirmwareBuffer == NULL)
|
if (tempFirmwareBuffer == NULL)
|
||||||
{
|
{
|
||||||
std::cout << "ERROR: Failed to allocate memory." << std::endl;
|
LOG_ERROR("Failed to allocate memory.", 0);
|
||||||
return K4A_RESULT_FAILED;
|
return K4A_RESULT_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "File size: " << tempFirmwareSize << " bytes" << std::endl;
|
LOG_INFO("Loaded: \"%s\", File size: %d bytes.", firmware_path, tempFirmwareSize);
|
||||||
numRead = fread(tempFirmwareBuffer, tempFirmwareSize, 1, pFirmwareFile);
|
numRead = fread(tempFirmwareBuffer, tempFirmwareSize, 1, pFirmwareFile);
|
||||||
fclose(pFirmwareFile);
|
fclose(pFirmwareFile);
|
||||||
|
|
||||||
if (numRead != 1)
|
if (numRead != 1)
|
||||||
{
|
{
|
||||||
std::cout << "ERROR: Could not read all data from the file" << std::endl;
|
LOG_ERROR("Failed to read all of the data from the file.", 0);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -237,7 +239,7 @@ k4a_result_t load_firmware_files(char *firmware_path, uint8_t **firmware_buffer,
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::cout << "ERROR: Cannot Open (" << firmware_path << ") errno=" << errno << std::endl;
|
LOG_ERROR("Failed to open \"%s\" errno=%d", firmware_path, errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tempFirmwareBuffer)
|
if (tempFirmwareBuffer)
|
||||||
|
@ -477,8 +479,12 @@ k4a_result_t open_firmware_device(firmware_t *firmware_handle)
|
||||||
|
|
||||||
while (device_count == 0 && retry++ < 20)
|
while (device_count == 0 && retry++ < 20)
|
||||||
{
|
{
|
||||||
ThreadAPI_Sleep(500);
|
|
||||||
K4A_TEST_VERIFY_SUCCEEDED(usb_cmd_get_device_count(&device_count));
|
K4A_TEST_VERIFY_SUCCEEDED(usb_cmd_get_device_count(&device_count));
|
||||||
|
|
||||||
|
if (device_count == 0)
|
||||||
|
{
|
||||||
|
ThreadAPI_Sleep(500);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
K4A_TEST_VERIFY_LE(retry, 20);
|
K4A_TEST_VERIFY_LE(retry, 20);
|
||||||
|
@ -498,6 +504,8 @@ k4a_result_t reset_device(firmware_t *firmware_handle)
|
||||||
firmware_destroy(*firmware_handle);
|
firmware_destroy(*firmware_handle);
|
||||||
*firmware_handle = nullptr;
|
*firmware_handle = nullptr;
|
||||||
|
|
||||||
|
ThreadAPI_Sleep(1000);
|
||||||
|
|
||||||
// Re-open the device to ensure it is ready for use.
|
// Re-open the device to ensure it is ready for use.
|
||||||
return TRACE_CALL(open_firmware_device(firmware_handle));
|
return TRACE_CALL(open_firmware_device(firmware_handle));
|
||||||
}
|
}
|
||||||
|
@ -508,13 +516,15 @@ k4a_result_t disconnect_device(firmware_t *firmware_handle)
|
||||||
|
|
||||||
K4A_TEST_VERIFY_SUCCEEDED(g_connection_exerciser->set_usb_port(0));
|
K4A_TEST_VERIFY_SUCCEEDED(g_connection_exerciser->set_usb_port(0));
|
||||||
|
|
||||||
ThreadAPI_Sleep(500);
|
ThreadAPI_Sleep(1000);
|
||||||
|
|
||||||
K4A_TEST_VERIFY_SUCCEEDED(g_connection_exerciser->set_usb_port(g_k4a_port_number));
|
K4A_TEST_VERIFY_SUCCEEDED(g_connection_exerciser->set_usb_port(g_k4a_port_number));
|
||||||
|
|
||||||
firmware_destroy(*firmware_handle);
|
firmware_destroy(*firmware_handle);
|
||||||
*firmware_handle = nullptr;
|
*firmware_handle = nullptr;
|
||||||
|
|
||||||
|
ThreadAPI_Sleep(1000);
|
||||||
|
|
||||||
// Re-open the device to ensure it is ready for use.
|
// Re-open the device to ensure it is ready for use.
|
||||||
return TRACE_CALL(open_firmware_device(firmware_handle));
|
return TRACE_CALL(open_firmware_device(firmware_handle));
|
||||||
}
|
}
|
||||||
|
@ -788,16 +798,58 @@ int main(int argc, char **argv)
|
||||||
argument[j] = (char)tolower(argument[j]);
|
argument[j] = (char)tolower(argument[j]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp(argument, "--firmware") == 0)
|
if (strcmp(argument, "-cf") == 0 || strcmp(argument, "--candidate-firmware") == 0)
|
||||||
{
|
{
|
||||||
if (i + 1 <= argc)
|
if (i + 1 <= argc)
|
||||||
{
|
{
|
||||||
g_candidate_firmware_path = argv[++i];
|
g_candidate_firmware_path = argv[++i];
|
||||||
printf("Setting g_test_firmware_path = %s\n", g_candidate_firmware_path);
|
printf("Setting g_candidate_firmware_path = %s\n", g_candidate_firmware_path);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
printf("Error: firmware path parameter missing\n");
|
printf("Error: candidate firmware path parameter missing\n");
|
||||||
|
error = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(argument, "-tf") == 0 || strcmp(argument, "--test-firmware") == 0)
|
||||||
|
{
|
||||||
|
if (i + 1 <= argc)
|
||||||
|
{
|
||||||
|
g_test_firmware_path = argv[++i];
|
||||||
|
printf("Setting g_test_firmware_path = %s\n", g_test_firmware_path);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("Error: test firmware path parameter missing\n");
|
||||||
|
error = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(argument, "-lf") == 0 || strcmp(argument, "--lkg-firmware") == 0)
|
||||||
|
{
|
||||||
|
if (i + 1 <= argc)
|
||||||
|
{
|
||||||
|
g_lkg_firmware_path = argv[++i];
|
||||||
|
printf("Setting g_lkg_firmware_path = %s\n", g_lkg_firmware_path);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("Error: lkg firmware path parameter missing\n");
|
||||||
|
error = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(argument, "-ff") == 0 || strcmp(argument, "--factory-firmware") == 0)
|
||||||
|
{
|
||||||
|
if (i + 1 <= argc)
|
||||||
|
{
|
||||||
|
g_factory_firmware_path = argv[++i];
|
||||||
|
printf("Setting g_factory_firmware_path = %s\n", g_factory_firmware_path);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("Error: factory firmware path parameter missing\n");
|
||||||
error = true;
|
error = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -812,8 +864,17 @@ int main(int argc, char **argv)
|
||||||
if (error)
|
if (error)
|
||||||
{
|
{
|
||||||
printf("\n\nCustom Test Settings:\n");
|
printf("\n\nCustom Test Settings:\n");
|
||||||
printf(" --firmware <firmware path>\n");
|
printf(" -cf | --candidate-firmware <firmware path>\n");
|
||||||
printf(" This is the path to the candidate firmware that should be tested.\n");
|
printf(" This is the path to the candidate firmware that should be tested.\n");
|
||||||
|
printf(" -tf | --test-firmware <firmware path>\n");
|
||||||
|
printf(" This is the path to the test firmware that will be used to test the candidate. It should have a "
|
||||||
|
"different version for all components.\n");
|
||||||
|
printf(" -lf | --lkg-firmware <firmware path>\n");
|
||||||
|
printf(" This is the path to the LKG firmware that will be used as a starting point for updating to the "
|
||||||
|
"candidate.\n");
|
||||||
|
printf(" -ff | --factory-firmware <firmware path>\n");
|
||||||
|
printf(" This is the path to the factory firmware that will be used as a starting point for updating to "
|
||||||
|
"the candidate.\n");
|
||||||
|
|
||||||
return 1; // Indicates an error or warning
|
return 1; // Indicates an error or warning
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,19 +11,6 @@
|
||||||
#define UPDATE_TIMEOUT_MS 10 * 60 * 1000 // 10 Minutes should be way more than enough.
|
#define UPDATE_TIMEOUT_MS 10 * 60 * 1000 // 10 Minutes should be way more than enough.
|
||||||
#define UPDATE_POLL_INTERVAL_MS 5
|
#define UPDATE_POLL_INTERVAL_MS 5
|
||||||
|
|
||||||
// This will define the path to the firmware packages to use in testing the firmware update process. The firmware update
|
|
||||||
// is executed by the firmware that is currently on the device. In order to test the firmware update process for a
|
|
||||||
// candidate, the device must be on the candidate firmware and then updated to a different test firmware where all of
|
|
||||||
// the versions are different.
|
|
||||||
// Factory firmware - This should be the oldest available firmware that we can roll back to.
|
|
||||||
// LKG firmware - This should be the last firmware that was released.
|
|
||||||
// Test firmware - This should be a firmware where all components have different versions than the candidate firmware.
|
|
||||||
// Candidate firmware - This should be the firmware that is being validated. It will be passed in via command line
|
|
||||||
// parameter.
|
|
||||||
#define K4A_FACTORY_FIRMWARE_PATH "D:\\Shares\\Eden\\Public\\AzureKinectDK_Fw_1.5.786013.bin"
|
|
||||||
#define K4A_LKG_FIRMWARE_PATH "D:\\Shares\\Eden\\Public\\AzureKinectDK_Fw_1.5.886314.bin"
|
|
||||||
#define K4A_TEST_FIRMWARE_PATH "D:\\Shares\\Eden\\Public\\AzureKinectDK_Fw_1.5.786013.bin"
|
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
FIRMWARE_OPERATION_START,
|
FIRMWARE_OPERATION_START,
|
||||||
|
@ -45,18 +32,28 @@ typedef enum
|
||||||
extern int g_k4a_port_number;
|
extern int g_k4a_port_number;
|
||||||
extern connection_exerciser *g_connection_exerciser;
|
extern connection_exerciser *g_connection_exerciser;
|
||||||
|
|
||||||
extern uint8_t *g_test_firmware_buffer;
|
// This will define the information about the relevant firmware packages to use in testing the update process. The
|
||||||
extern size_t g_test_firmware_size;
|
// firmware update is executed by the firmware that is currently on the device. In order to test the firmware update
|
||||||
extern firmware_package_info_t g_test_firmware_package_info;
|
// process for a candidate, the device must be on the candidate firmware and then updated to a different test firmware
|
||||||
|
// where all of the versions are different.
|
||||||
|
|
||||||
|
// Candidate firmware - This is the firmware that is being validated.
|
||||||
extern uint8_t *g_candidate_firmware_buffer;
|
extern uint8_t *g_candidate_firmware_buffer;
|
||||||
extern size_t g_candidate_firmware_size;
|
extern size_t g_candidate_firmware_size;
|
||||||
extern firmware_package_info_t g_candidate_firmware_package_info;
|
extern firmware_package_info_t g_candidate_firmware_package_info;
|
||||||
|
|
||||||
|
// Test firmware - This is the firmware being used to test the Candidate. All components will have different versions
|
||||||
|
// than the candidate firmware.
|
||||||
|
extern uint8_t *g_test_firmware_buffer;
|
||||||
|
extern size_t g_test_firmware_size;
|
||||||
|
extern firmware_package_info_t g_test_firmware_package_info;
|
||||||
|
|
||||||
|
// LKG firmware - This should be the last known good firmware that was released.
|
||||||
extern uint8_t *g_lkg_firmware_buffer;
|
extern uint8_t *g_lkg_firmware_buffer;
|
||||||
extern size_t g_lkg_firmware_size;
|
extern size_t g_lkg_firmware_size;
|
||||||
extern firmware_package_info_t g_lkg_firmware_package_info;
|
extern firmware_package_info_t g_lkg_firmware_package_info;
|
||||||
|
|
||||||
|
// Factory firmware - This should be the oldest available firmware that we can roll back to.
|
||||||
extern uint8_t *g_factory_firmware_buffer;
|
extern uint8_t *g_factory_firmware_buffer;
|
||||||
extern size_t g_factory_firmware_size;
|
extern size_t g_factory_firmware_size;
|
||||||
extern firmware_package_info_t g_factory_firmware_package_info;
|
extern firmware_package_info_t g_factory_firmware_package_info;
|
||||||
|
|
|
@ -31,7 +31,7 @@ protected:
|
||||||
{
|
{
|
||||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED, TRACE_CALL(setup_common_test()));
|
ASSERT_EQ(K4A_RESULT_SUCCEEDED, TRACE_CALL(setup_common_test()));
|
||||||
const ::testing::TestInfo *const test_info = ::testing::UnitTest::GetInstance()->current_test_info();
|
const ::testing::TestInfo *const test_info = ::testing::UnitTest::GetInstance()->current_test_info();
|
||||||
LOG_INFO("Test %s requires a connection exerciser.", test_info->name());
|
printf("\nStarting test %s. This requires a connection exerciser to be connected.\n", test_info->name());
|
||||||
LOG_INFO("Disconnecting the device", 0);
|
LOG_INFO("Disconnecting the device", 0);
|
||||||
g_connection_exerciser->set_usb_port(0);
|
g_connection_exerciser->set_usb_port(0);
|
||||||
ThreadAPI_Sleep(500);
|
ThreadAPI_Sleep(500);
|
||||||
|
@ -51,7 +51,7 @@ protected:
|
||||||
ASSERT_FALSE(compare_version(g_test_firmware_package_info.depth, g_candidate_firmware_package_info.depth));
|
ASSERT_FALSE(compare_version(g_test_firmware_package_info.depth, g_candidate_firmware_package_info.depth));
|
||||||
ASSERT_FALSE(compare_version(g_test_firmware_package_info.rgb, g_candidate_firmware_package_info.rgb));
|
ASSERT_FALSE(compare_version(g_test_firmware_package_info.rgb, g_candidate_firmware_package_info.rgb));
|
||||||
|
|
||||||
// There should be no other devices.
|
// There should be no other devices as the tests use the default device to connect to.
|
||||||
uint32_t device_count = 0;
|
uint32_t device_count = 0;
|
||||||
usb_cmd_get_device_count(&device_count);
|
usb_cmd_get_device_count(&device_count);
|
||||||
ASSERT_EQ((uint32_t)0, device_count);
|
ASSERT_EQ((uint32_t)0, device_count);
|
||||||
|
@ -83,14 +83,14 @@ TEST_P(firmware_interrupt_fw, interrupt_update)
|
||||||
{
|
{
|
||||||
firmware_interrupt_parameters parameters = GetParam();
|
firmware_interrupt_parameters parameters = GetParam();
|
||||||
firmware_status_summary_t finalStatus;
|
firmware_status_summary_t finalStatus;
|
||||||
LOG_INFO("Beginning the \'%s\' test. Stage: %d Interruption: %d",
|
|
||||||
parameters.test_name,
|
printf("Beginning the \'%s\' test. Stage: %d Interruption: %d",
|
||||||
parameters.component,
|
parameters.test_name,
|
||||||
parameters.interruption);
|
parameters.component,
|
||||||
|
parameters.interruption);
|
||||||
|
|
||||||
LOG_INFO("Powering on the device...", 0);
|
LOG_INFO("Powering on the device...", 0);
|
||||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED, g_connection_exerciser->set_usb_port(g_k4a_port_number));
|
ASSERT_EQ(K4A_RESULT_SUCCEEDED, g_connection_exerciser->set_usb_port(g_k4a_port_number));
|
||||||
|
|
||||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED, open_firmware_device(&firmware_handle));
|
ASSERT_EQ(K4A_RESULT_SUCCEEDED, open_firmware_device(&firmware_handle));
|
||||||
|
|
||||||
ASSERT_EQ(K4A_BUFFER_RESULT_TOO_SMALL, firmware_get_device_serialnum(firmware_handle, NULL, &serial_number_length));
|
ASSERT_EQ(K4A_BUFFER_RESULT_TOO_SMALL, firmware_get_device_serialnum(firmware_handle, NULL, &serial_number_length));
|
||||||
|
@ -102,7 +102,7 @@ TEST_P(firmware_interrupt_fw, interrupt_update)
|
||||||
firmware_get_device_serialnum(firmware_handle, serial_number, &serial_number_length));
|
firmware_get_device_serialnum(firmware_handle, serial_number, &serial_number_length));
|
||||||
|
|
||||||
// Update to the Candidate firmware
|
// Update to the Candidate firmware
|
||||||
LOG_INFO("Updating the device to the Candidate firmware.");
|
printf("\n == Updating the device to the Candidate firmware.\n");
|
||||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED,
|
ASSERT_EQ(K4A_RESULT_SUCCEEDED,
|
||||||
perform_device_update(&firmware_handle,
|
perform_device_update(&firmware_handle,
|
||||||
g_candidate_firmware_buffer,
|
g_candidate_firmware_buffer,
|
||||||
|
@ -111,7 +111,7 @@ TEST_P(firmware_interrupt_fw, interrupt_update)
|
||||||
false));
|
false));
|
||||||
|
|
||||||
// Update to the Test firmware, but interrupt...
|
// Update to the Test firmware, but interrupt...
|
||||||
LOG_INFO("Beginning of the firmware update to the Test Firmware with interruption...");
|
printf("\n == Beginning of the firmware update to the Test Firmware with interruption...\n");
|
||||||
|
|
||||||
// Prepend the "Firmware Package Versions:\n" with "Test".
|
// Prepend the "Firmware Package Versions:\n" with "Test".
|
||||||
printf("Test ");
|
printf("Test ");
|
||||||
|
@ -190,7 +190,7 @@ TEST_P(firmware_interrupt_fw, interrupt_update)
|
||||||
// happened.
|
// happened.
|
||||||
if (!compare_version(current_version.depth, { 0 }))
|
if (!compare_version(current_version.depth, { 0 }))
|
||||||
{
|
{
|
||||||
printf(" The Depth version was not expected\n");
|
printf(" ** The Depth version was not expected\n");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -205,15 +205,15 @@ TEST_P(firmware_interrupt_fw, interrupt_update)
|
||||||
// reset actually happened.
|
// reset actually happened.
|
||||||
if (!compare_version(current_version.audio, g_test_firmware_package_info.audio))
|
if (!compare_version(current_version.audio, g_test_firmware_package_info.audio))
|
||||||
{
|
{
|
||||||
printf(" The Audio version was not expected\n");
|
printf(" ** The Audio version was not expected\n");
|
||||||
}
|
}
|
||||||
if (!compare_version(current_version.depth, g_test_firmware_package_info.depth))
|
if (!compare_version(current_version.depth, g_test_firmware_package_info.depth))
|
||||||
{
|
{
|
||||||
printf(" The Depth version was not expected\n");
|
printf(" ** The Depth version was not expected\n");
|
||||||
}
|
}
|
||||||
if (!compare_version(current_version.rgb, { 0 }))
|
if (!compare_version(current_version.rgb, { 0 }))
|
||||||
{
|
{
|
||||||
printf(" The RGB version was not expected\n");
|
printf(" ** The RGB version was not expected\n");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -222,7 +222,7 @@ TEST_P(firmware_interrupt_fw, interrupt_update)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update back to the LKG firmware to make sure that works.
|
// Update back to the LKG firmware to make sure that works.
|
||||||
LOG_INFO("Updating the device back to the LKG firmware.");
|
printf("\n == Updating the device back to the LKG firmware.\n");
|
||||||
ASSERT_EQ(K4A_RESULT_SUCCEEDED,
|
ASSERT_EQ(K4A_RESULT_SUCCEEDED,
|
||||||
perform_device_update(&firmware_handle,
|
perform_device_update(&firmware_handle,
|
||||||
g_lkg_firmware_buffer,
|
g_lkg_firmware_buffer,
|
||||||
|
|
|
@ -36,7 +36,7 @@ TEST_F(playback_perf, test_open)
|
||||||
k4a_record_configuration_t config;
|
k4a_record_configuration_t config;
|
||||||
result = k4a_playback_get_record_configuration(handle, &config);
|
result = k4a_playback_get_record_configuration(handle, &config);
|
||||||
ASSERT_EQ(result, K4A_RESULT_SUCCEEDED);
|
ASSERT_EQ(result, K4A_RESULT_SUCCEEDED);
|
||||||
std::cout << "Config:" << std::endl;
|
std::cout << "Record config:" << std::endl;
|
||||||
std::cout << " Tracks enabled:";
|
std::cout << " Tracks enabled:";
|
||||||
static const std::pair<bool *, std::string> tracks[] = { { &config.color_track_enabled, "Color" },
|
static const std::pair<bool *, std::string> tracks[] = { { &config.color_track_enabled, "Color" },
|
||||||
{ &config.depth_track_enabled, "Depth" },
|
{ &config.depth_track_enabled, "Depth" },
|
||||||
|
@ -57,6 +57,58 @@ TEST_F(playback_perf, test_open)
|
||||||
std::cout << " Depth delay: " << config.depth_delay_off_color_usec << " usec" << std::endl;
|
std::cout << " Depth delay: " << config.depth_delay_off_color_usec << " usec" << std::endl;
|
||||||
std::cout << " Start offset: " << config.start_timestamp_offset_usec << " usec" << std::endl;
|
std::cout << " Start offset: " << config.start_timestamp_offset_usec << " usec" << std::endl;
|
||||||
|
|
||||||
|
std::pair<k4a_image_t, std::string> images[] = { { NULL, "Color" }, { NULL, "Depth" }, { NULL, "IR" } };
|
||||||
|
while (images[0].first == NULL || images[1].first == NULL || images[2].first == NULL)
|
||||||
|
{
|
||||||
|
k4a_capture_t capture = NULL;
|
||||||
|
k4a_stream_result_t playback_result = k4a_playback_get_next_capture(handle, &capture);
|
||||||
|
ASSERT_NE(playback_result, K4A_STREAM_RESULT_FAILED);
|
||||||
|
if (playback_result == K4A_STREAM_RESULT_EOF)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ASSERT_NE(capture, nullptr);
|
||||||
|
if (images[0].first == NULL)
|
||||||
|
{
|
||||||
|
images[0].first = k4a_capture_get_color_image(capture);
|
||||||
|
}
|
||||||
|
if (images[1].first == NULL)
|
||||||
|
{
|
||||||
|
images[1].first = k4a_capture_get_depth_image(capture);
|
||||||
|
}
|
||||||
|
if (images[2].first == NULL)
|
||||||
|
{
|
||||||
|
images[2].first = k4a_capture_get_ir_image(capture);
|
||||||
|
}
|
||||||
|
k4a_capture_release(capture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (images[0].first == NULL && images[1].first == NULL && images[2].first == NULL)
|
||||||
|
{
|
||||||
|
std::cout << std::endl;
|
||||||
|
std::cout << "Input file has no captures." << std::endl;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < arraysize(images); i++)
|
||||||
|
{
|
||||||
|
if (images[i].first)
|
||||||
|
{
|
||||||
|
std::cout << std::endl;
|
||||||
|
std::cout << "First " << images[i].second << " image:" << std::endl;
|
||||||
|
std::cout << " Timestamp: " << k4a_image_get_timestamp_usec(images[i].first) << " usec" << std::endl;
|
||||||
|
std::cout << " Image format: " << format_names[k4a_image_get_format(images[i].first)] << std::endl;
|
||||||
|
std::cout << " Resolution: " << k4a_image_get_width_pixels(images[i].first) << "x"
|
||||||
|
<< k4a_image_get_height_pixels(images[i].first) << std::endl;
|
||||||
|
std::cout << " Buffer size: " << k4a_image_get_size(images[i].first)
|
||||||
|
<< " (stride: " << k4a_image_get_stride_bytes(images[i].first) << " bytes)" << std::endl;
|
||||||
|
k4a_image_release(images[i].first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
k4a_playback_close(handle);
|
k4a_playback_close(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,12 +129,38 @@ TEST_F(playback_perf, test_1000_reads_forward)
|
||||||
for (int i = 0; i < 1000; i++)
|
for (int i = 0; i < 1000; i++)
|
||||||
{
|
{
|
||||||
playback_result = k4a_playback_get_next_capture(handle, &capture);
|
playback_result = k4a_playback_get_next_capture(handle, &capture);
|
||||||
ASSERT_EQ(playback_result, K4A_STREAM_RESULT_SUCCEEDED);
|
ASSERT_NE(playback_result, K4A_STREAM_RESULT_FAILED);
|
||||||
|
if (playback_result == K4A_STREAM_RESULT_EOF)
|
||||||
|
{
|
||||||
|
std::cout << " Warning: Input file is too short, only read " << i << " captures." << std::endl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
ASSERT_NE(capture, nullptr);
|
ASSERT_NE(capture, nullptr);
|
||||||
k4a_capture_release(capture);
|
k4a_capture_release(capture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
k4a_record_configuration_t config;
|
||||||
|
result = k4a_playback_get_record_configuration(handle, &config);
|
||||||
|
ASSERT_EQ(result, K4A_RESULT_SUCCEEDED);
|
||||||
|
|
||||||
|
if (config.imu_track_enabled)
|
||||||
|
{
|
||||||
|
k4a_imu_sample_t sample = { 0 };
|
||||||
|
k4a_stream_result_t playback_result = K4A_STREAM_RESULT_FAILED;
|
||||||
|
Timer t("Next imu smaple x10000");
|
||||||
|
for (int i = 0; i < 10000; i++)
|
||||||
|
{
|
||||||
|
playback_result = k4a_playback_get_next_imu_sample(handle, &sample);
|
||||||
|
ASSERT_NE(playback_result, K4A_STREAM_RESULT_FAILED);
|
||||||
|
if (playback_result == K4A_STREAM_RESULT_EOF)
|
||||||
|
{
|
||||||
|
std::cout << " Warning: Input file is too short, only read " << i << " imu_samples." << std::endl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
k4a_playback_close(handle);
|
k4a_playback_close(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,12 +187,38 @@ TEST_F(playback_perf, test_1000_reads_backward)
|
||||||
for (int i = 0; i < 1000; i++)
|
for (int i = 0; i < 1000; i++)
|
||||||
{
|
{
|
||||||
playback_result = k4a_playback_get_previous_capture(handle, &capture);
|
playback_result = k4a_playback_get_previous_capture(handle, &capture);
|
||||||
ASSERT_EQ(playback_result, K4A_STREAM_RESULT_SUCCEEDED);
|
ASSERT_NE(playback_result, K4A_STREAM_RESULT_FAILED);
|
||||||
|
if (playback_result == K4A_STREAM_RESULT_EOF)
|
||||||
|
{
|
||||||
|
std::cout << " Warning: Input file is too short, only read " << i << " captures." << std::endl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
ASSERT_NE(capture, nullptr);
|
ASSERT_NE(capture, nullptr);
|
||||||
k4a_capture_release(capture);
|
k4a_capture_release(capture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
k4a_record_configuration_t config;
|
||||||
|
result = k4a_playback_get_record_configuration(handle, &config);
|
||||||
|
ASSERT_EQ(result, K4A_RESULT_SUCCEEDED);
|
||||||
|
|
||||||
|
if (config.imu_track_enabled)
|
||||||
|
{
|
||||||
|
k4a_imu_sample_t sample = { 0 };
|
||||||
|
k4a_stream_result_t playback_result = K4A_STREAM_RESULT_FAILED;
|
||||||
|
Timer t("Previous imu smaple x10000");
|
||||||
|
for (int i = 0; i < 10000; i++)
|
||||||
|
{
|
||||||
|
playback_result = k4a_playback_get_previous_imu_sample(handle, &sample);
|
||||||
|
ASSERT_NE(playback_result, K4A_STREAM_RESULT_FAILED);
|
||||||
|
if (playback_result == K4A_STREAM_RESULT_EOF)
|
||||||
|
{
|
||||||
|
std::cout << " Warning: Input file is too short, only read " << i << " imu_samples." << std::endl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
k4a_playback_close(handle);
|
k4a_playback_close(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,7 +245,12 @@ TEST_F(playback_perf, test_read_latency_30fps)
|
||||||
playback_result = k4a_playback_get_next_capture(handle, &capture);
|
playback_result = k4a_playback_get_next_capture(handle, &capture);
|
||||||
auto delta = std::chrono::high_resolution_clock::now() - start;
|
auto delta = std::chrono::high_resolution_clock::now() - start;
|
||||||
|
|
||||||
ASSERT_EQ(playback_result, K4A_STREAM_RESULT_SUCCEEDED);
|
ASSERT_NE(playback_result, K4A_STREAM_RESULT_FAILED);
|
||||||
|
if (playback_result == K4A_STREAM_RESULT_EOF)
|
||||||
|
{
|
||||||
|
std::cout << " Warning: Input file is too short, only read " << i << " captures." << std::endl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
ASSERT_NE(capture, nullptr);
|
ASSERT_NE(capture, nullptr);
|
||||||
k4a_capture_release(capture);
|
k4a_capture_release(capture);
|
||||||
|
|
||||||
|
@ -156,9 +265,65 @@ TEST_F(playback_perf, test_read_latency_30fps)
|
||||||
{
|
{
|
||||||
total_ns += d;
|
total_ns += d;
|
||||||
}
|
}
|
||||||
std::cout << "Avg latency: " << (total_ns / (int64_t)deltas.size() / 1000) << " usec" << std::endl;
|
std::cout << " Avg latency: " << (total_ns / (int64_t)deltas.size() / 1000) << " usec" << std::endl;
|
||||||
std::cout << "P95 latency: " << (deltas[(size_t)((double)deltas.size() * 0.95) - 1] / 1000) << " usec" << std::endl;
|
std::cout << " P95 latency: " << (deltas[(size_t)((double)deltas.size() * 0.95) - 1] / 1000) << " usec"
|
||||||
std::cout << "P99 latency: " << (deltas[(size_t)((double)deltas.size() * 0.99) - 1] / 1000) << " usec" << std::endl;
|
<< std::endl;
|
||||||
|
std::cout << " P99 latency: " << (deltas[(size_t)((double)deltas.size() * 0.99) - 1] / 1000) << " usec"
|
||||||
|
<< std::endl;
|
||||||
|
|
||||||
|
k4a_playback_close(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(playback_perf, test_read_latency_30fps_bgra_conversion)
|
||||||
|
{
|
||||||
|
k4a_playback_t handle = NULL;
|
||||||
|
k4a_result_t result = K4A_RESULT_FAILED;
|
||||||
|
{
|
||||||
|
Timer t("File open: " + g_test_file_name);
|
||||||
|
result = k4a_playback_open(g_test_file_name.c_str(), &handle);
|
||||||
|
}
|
||||||
|
ASSERT_EQ(result, K4A_RESULT_SUCCEEDED);
|
||||||
|
|
||||||
|
result = k4a_playback_set_color_conversion(handle, K4A_IMAGE_FORMAT_COLOR_BGRA32);
|
||||||
|
ASSERT_EQ(result, K4A_RESULT_SUCCEEDED);
|
||||||
|
|
||||||
|
std::vector<int64_t> deltas;
|
||||||
|
|
||||||
|
{
|
||||||
|
k4a_capture_t capture = NULL;
|
||||||
|
k4a_stream_result_t playback_result = K4A_STREAM_RESULT_FAILED;
|
||||||
|
Timer t("Next capture x1000");
|
||||||
|
for (int i = 0; i < 1000; i++)
|
||||||
|
{
|
||||||
|
|
||||||
|
auto start = std::chrono::high_resolution_clock::now();
|
||||||
|
playback_result = k4a_playback_get_next_capture(handle, &capture);
|
||||||
|
auto delta = std::chrono::high_resolution_clock::now() - start;
|
||||||
|
|
||||||
|
if (playback_result == K4A_STREAM_RESULT_EOF)
|
||||||
|
{
|
||||||
|
std::cout << " Warning: Input file is too short, only read " << i << " captures." << std::endl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ASSERT_NE(capture, nullptr);
|
||||||
|
k4a_capture_release(capture);
|
||||||
|
|
||||||
|
deltas.push_back(delta.count());
|
||||||
|
std::this_thread::sleep_until(start + std::chrono::milliseconds(33));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort(deltas.begin(), deltas.end(), std::less<int64_t>());
|
||||||
|
int64_t total_ns = 0;
|
||||||
|
for (auto d : deltas)
|
||||||
|
{
|
||||||
|
total_ns += d;
|
||||||
|
}
|
||||||
|
std::cout << " Avg latency: " << (total_ns / (int64_t)deltas.size() / 1000) << " usec" << std::endl;
|
||||||
|
std::cout << " P95 latency: " << (deltas[(size_t)((double)deltas.size() * 0.95) - 1] / 1000) << " usec"
|
||||||
|
<< std::endl;
|
||||||
|
std::cout << " P99 latency: " << (deltas[(size_t)((double)deltas.size() * 0.99) - 1] / 1000) << " usec"
|
||||||
|
<< std::endl;
|
||||||
|
|
||||||
k4a_playback_close(handle);
|
k4a_playback_close(handle);
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,12 @@ protected:
|
||||||
|
|
||||||
void TearDown() override
|
void TearDown() override
|
||||||
{
|
{
|
||||||
|
for (cluster_t *cluster : *context->pending_clusters)
|
||||||
|
{
|
||||||
|
delete cluster;
|
||||||
|
}
|
||||||
|
context->pending_clusters->clear();
|
||||||
|
|
||||||
k4a_record_t_destroy(recording_handle);
|
k4a_record_t_destroy(recording_handle);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -353,7 +353,7 @@ TEST_F(transformation_ut, transformation_depth_image_to_point_cloud)
|
||||||
|
|
||||||
// Comparison against reference hash value computed over the entire image. If result image is changed (e.g., due to
|
// Comparison against reference hash value computed over the entire image. If result image is changed (e.g., due to
|
||||||
// using a different calibration), the reference value needs to be updated.
|
// using a different calibration), the reference value needs to be updated.
|
||||||
const double reference_val = 633.99727884928382;
|
const double reference_val = 562.20976003011071;
|
||||||
if (std::abs(check_sum - reference_val) > 0.001)
|
if (std::abs(check_sum - reference_val) > 0.001)
|
||||||
{
|
{
|
||||||
ASSERT_EQ(check_sum, reference_val);
|
ASSERT_EQ(check_sum, reference_val);
|
||||||
|
@ -364,19 +364,8 @@ TEST_F(transformation_ut, transformation_depth_image_to_point_cloud)
|
||||||
transformation_destroy(transformation_handle);
|
transformation_destroy(transformation_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(transformation_ut, transformation_create_depth_only)
|
TEST_F(transformation_ut, transformation_all_image_functions_with_failure_cases)
|
||||||
{
|
{
|
||||||
k4a_depth_mode_t depth_mode = K4A_DEPTH_MODE_NFOV_UNBINNED;
|
|
||||||
k4a_color_resolution_t color_resolution = K4A_COLOR_RESOLUTION_OFF;
|
|
||||||
|
|
||||||
k4a_calibration_t calibration;
|
|
||||||
k4a_result_t result =
|
|
||||||
k4a_calibration_get_from_raw(g_test_json, sizeof(g_test_json), depth_mode, color_resolution, &calibration);
|
|
||||||
ASSERT_EQ(result, K4A_RESULT_SUCCEEDED);
|
|
||||||
|
|
||||||
k4a_transformation_t transformation_handle = transformation_create(&calibration, true);
|
|
||||||
ASSERT_NE(transformation_handle, (k4a_transformation_t)NULL);
|
|
||||||
|
|
||||||
int depth_image_width_pixels = 640;
|
int depth_image_width_pixels = 640;
|
||||||
int depth_image_height_pixels = 576;
|
int depth_image_height_pixels = 576;
|
||||||
k4a_image_t depth_image = NULL;
|
k4a_image_t depth_image = NULL;
|
||||||
|
@ -387,8 +376,8 @@ TEST_F(transformation_ut, transformation_create_depth_only)
|
||||||
&depth_image),
|
&depth_image),
|
||||||
K4A_WAIT_RESULT_SUCCEEDED);
|
K4A_WAIT_RESULT_SUCCEEDED);
|
||||||
|
|
||||||
int color_image_width_pixels = 1920;
|
int color_image_width_pixels = 1280;
|
||||||
int color_image_height_pixels = 1080;
|
int color_image_height_pixels = 720;
|
||||||
k4a_image_t color_image = NULL;
|
k4a_image_t color_image = NULL;
|
||||||
ASSERT_EQ(image_create(K4A_IMAGE_FORMAT_DEPTH16,
|
ASSERT_EQ(image_create(K4A_IMAGE_FORMAT_DEPTH16,
|
||||||
color_image_width_pixels,
|
color_image_width_pixels,
|
||||||
|
@ -413,151 +402,142 @@ TEST_F(transformation_ut, transformation_create_depth_only)
|
||||||
&transformed_depth_image),
|
&transformed_depth_image),
|
||||||
K4A_WAIT_RESULT_SUCCEEDED);
|
K4A_WAIT_RESULT_SUCCEEDED);
|
||||||
|
|
||||||
k4a_transformation_image_descriptor_t depth_image_descriptor = image_get_descriptor(depth_image);
|
k4a_image_t xyz_depth_image = NULL;
|
||||||
k4a_transformation_image_descriptor_t color_image_descriptor = image_get_descriptor(color_image);
|
|
||||||
k4a_transformation_image_descriptor_t transformed_color_image_descriptor = image_get_descriptor(
|
|
||||||
transformed_color_image);
|
|
||||||
uint8_t *depth_image_buffer = image_get_buffer(depth_image);
|
|
||||||
uint8_t *color_image_buffer = image_get_buffer(color_image);
|
|
||||||
uint8_t *transformed_color_image_buffer = image_get_buffer(transformed_color_image);
|
|
||||||
ASSERT_NE(transformation_color_image_to_depth_camera(transformation_handle,
|
|
||||||
depth_image_buffer,
|
|
||||||
&depth_image_descriptor,
|
|
||||||
color_image_buffer,
|
|
||||||
&color_image_descriptor,
|
|
||||||
transformed_color_image_buffer,
|
|
||||||
&transformed_color_image_descriptor),
|
|
||||||
K4A_RESULT_SUCCEEDED);
|
|
||||||
|
|
||||||
k4a_transformation_image_descriptor_t transformed_depth_image_descriptor = image_get_descriptor(
|
|
||||||
transformed_depth_image);
|
|
||||||
uint8_t *transformed_depth_image_buffer = image_get_buffer(transformed_depth_image);
|
|
||||||
ASSERT_NE(transformation_depth_image_to_color_camera(transformation_handle,
|
|
||||||
depth_image_buffer,
|
|
||||||
&depth_image_descriptor,
|
|
||||||
transformed_depth_image_buffer,
|
|
||||||
&transformed_depth_image_descriptor),
|
|
||||||
K4A_RESULT_SUCCEEDED);
|
|
||||||
|
|
||||||
k4a_image_t point_cloud_image = NULL;
|
|
||||||
ASSERT_EQ(image_create(K4A_IMAGE_FORMAT_CUSTOM,
|
ASSERT_EQ(image_create(K4A_IMAGE_FORMAT_CUSTOM,
|
||||||
depth_image_width_pixels,
|
depth_image_width_pixels,
|
||||||
depth_image_height_pixels,
|
depth_image_height_pixels,
|
||||||
depth_image_width_pixels * 3 * (int)sizeof(int16_t),
|
depth_image_width_pixels * 3 * (int)sizeof(int16_t),
|
||||||
&point_cloud_image),
|
&xyz_depth_image),
|
||||||
K4A_WAIT_RESULT_SUCCEEDED);
|
|
||||||
|
|
||||||
k4a_transformation_image_descriptor_t point_cloud_image_descriptor = image_get_descriptor(point_cloud_image);
|
|
||||||
uint8_t *point_cloud_image_buffer = image_get_buffer(point_cloud_image);
|
|
||||||
ASSERT_EQ(transformation_depth_image_to_point_cloud(transformation_handle,
|
|
||||||
depth_image_buffer,
|
|
||||||
&depth_image_descriptor,
|
|
||||||
K4A_CALIBRATION_TYPE_DEPTH,
|
|
||||||
point_cloud_image_buffer,
|
|
||||||
&point_cloud_image_descriptor),
|
|
||||||
K4A_RESULT_SUCCEEDED);
|
K4A_RESULT_SUCCEEDED);
|
||||||
|
|
||||||
image_dec_ref(depth_image);
|
k4a_image_t xyz_color_image = NULL;
|
||||||
image_dec_ref(color_image);
|
ASSERT_EQ(image_create(K4A_IMAGE_FORMAT_CUSTOM,
|
||||||
image_dec_ref(transformed_color_image);
|
|
||||||
image_dec_ref(transformed_depth_image);
|
|
||||||
image_dec_ref(point_cloud_image);
|
|
||||||
transformation_destroy(transformation_handle);
|
|
||||||
image_dec_ref(depth_image);
|
|
||||||
image_dec_ref(color_image);
|
|
||||||
image_dec_ref(transformed_color_image);
|
|
||||||
image_dec_ref(transformed_depth_image);
|
|
||||||
image_dec_ref(point_cloud_image);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(transformation_ut, transformation_create_color_only)
|
|
||||||
{
|
|
||||||
k4a_depth_mode_t depth_mode = K4A_DEPTH_MODE_OFF;
|
|
||||||
k4a_color_resolution_t color_resolution = K4A_COLOR_RESOLUTION_720P;
|
|
||||||
|
|
||||||
k4a_calibration_t calibration;
|
|
||||||
k4a_result_t result =
|
|
||||||
k4a_calibration_get_from_raw(g_test_json, sizeof(g_test_json), depth_mode, color_resolution, &calibration);
|
|
||||||
ASSERT_EQ(result, K4A_RESULT_SUCCEEDED);
|
|
||||||
|
|
||||||
k4a_transformation_t transformation_handle = transformation_create(&calibration, true);
|
|
||||||
ASSERT_NE(transformation_handle, (k4a_transformation_t)NULL);
|
|
||||||
|
|
||||||
int depth_image_width_pixels = 640;
|
|
||||||
int depth_image_height_pixels = 576;
|
|
||||||
k4a_image_t depth_image = NULL;
|
|
||||||
ASSERT_EQ(image_create(K4A_IMAGE_FORMAT_DEPTH16,
|
|
||||||
depth_image_width_pixels,
|
|
||||||
depth_image_height_pixels,
|
|
||||||
depth_image_width_pixels * 1 * (int)sizeof(uint16_t),
|
|
||||||
&depth_image),
|
|
||||||
K4A_WAIT_RESULT_SUCCEEDED);
|
|
||||||
|
|
||||||
int color_image_width_pixels = 1920;
|
|
||||||
int color_image_height_pixels = 1080;
|
|
||||||
k4a_image_t color_image = NULL;
|
|
||||||
ASSERT_EQ(image_create(K4A_IMAGE_FORMAT_DEPTH16,
|
|
||||||
color_image_width_pixels,
|
color_image_width_pixels,
|
||||||
color_image_height_pixels,
|
color_image_height_pixels,
|
||||||
color_image_width_pixels * 4 * (int)sizeof(uint8_t),
|
color_image_width_pixels * 3 * (int)sizeof(int16_t),
|
||||||
&color_image),
|
&xyz_color_image),
|
||||||
K4A_WAIT_RESULT_SUCCEEDED);
|
K4A_RESULT_SUCCEEDED);
|
||||||
|
|
||||||
k4a_image_t transformed_color_image = NULL;
|
|
||||||
ASSERT_EQ(image_create(K4A_IMAGE_FORMAT_COLOR_BGRA32,
|
|
||||||
depth_image_width_pixels,
|
|
||||||
depth_image_height_pixels,
|
|
||||||
depth_image_width_pixels * 4 * (int)sizeof(uint8_t),
|
|
||||||
&transformed_color_image),
|
|
||||||
K4A_WAIT_RESULT_SUCCEEDED);
|
|
||||||
|
|
||||||
k4a_image_t transformed_depth_image = NULL;
|
|
||||||
ASSERT_EQ(image_create(K4A_IMAGE_FORMAT_DEPTH16,
|
|
||||||
color_image_width_pixels,
|
|
||||||
color_image_height_pixels,
|
|
||||||
color_image_width_pixels * 1 * (int)sizeof(uint16_t),
|
|
||||||
&transformed_depth_image),
|
|
||||||
K4A_WAIT_RESULT_SUCCEEDED);
|
|
||||||
|
|
||||||
k4a_transformation_image_descriptor_t depth_image_descriptor = image_get_descriptor(depth_image);
|
k4a_transformation_image_descriptor_t depth_image_descriptor = image_get_descriptor(depth_image);
|
||||||
k4a_transformation_image_descriptor_t color_image_descriptor = image_get_descriptor(color_image);
|
k4a_transformation_image_descriptor_t color_image_descriptor = image_get_descriptor(color_image);
|
||||||
k4a_transformation_image_descriptor_t transformed_color_image_descriptor = image_get_descriptor(
|
k4a_transformation_image_descriptor_t transformed_color_image_descriptor = image_get_descriptor(
|
||||||
transformed_color_image);
|
transformed_color_image);
|
||||||
uint8_t *depth_image_buffer = image_get_buffer(depth_image);
|
|
||||||
uint8_t *color_image_buffer = image_get_buffer(color_image);
|
|
||||||
uint8_t *transformed_color_image_buffer = image_get_buffer(transformed_color_image);
|
|
||||||
ASSERT_NE(transformation_color_image_to_depth_camera(transformation_handle,
|
|
||||||
depth_image_buffer,
|
|
||||||
&depth_image_descriptor,
|
|
||||||
color_image_buffer,
|
|
||||||
&color_image_descriptor,
|
|
||||||
transformed_color_image_buffer,
|
|
||||||
&transformed_color_image_descriptor),
|
|
||||||
K4A_RESULT_SUCCEEDED);
|
|
||||||
|
|
||||||
k4a_transformation_image_descriptor_t transformed_depth_image_descriptor = image_get_descriptor(
|
k4a_transformation_image_descriptor_t transformed_depth_image_descriptor = image_get_descriptor(
|
||||||
transformed_depth_image);
|
transformed_depth_image);
|
||||||
|
k4a_transformation_image_descriptor_t xyz_depth_image_descriptor = image_get_descriptor(xyz_depth_image);
|
||||||
|
k4a_transformation_image_descriptor_t xyz_color_image_descriptor = image_get_descriptor(xyz_color_image);
|
||||||
|
|
||||||
|
uint8_t *depth_image_buffer = image_get_buffer(depth_image);
|
||||||
|
uint8_t *color_image_buffer = image_get_buffer(color_image);
|
||||||
uint8_t *transformed_depth_image_buffer = image_get_buffer(transformed_depth_image);
|
uint8_t *transformed_depth_image_buffer = image_get_buffer(transformed_depth_image);
|
||||||
ASSERT_NE(transformation_depth_image_to_color_camera(transformation_handle,
|
uint8_t *transformed_color_image_buffer = image_get_buffer(transformed_color_image);
|
||||||
depth_image_buffer,
|
uint8_t *xyz_depth_image_buffer = image_get_buffer(xyz_depth_image);
|
||||||
&depth_image_descriptor,
|
uint8_t *xyz_color_image_buffer = image_get_buffer(xyz_color_image);
|
||||||
transformed_depth_image_buffer,
|
|
||||||
&transformed_depth_image_descriptor),
|
|
||||||
K4A_RESULT_SUCCEEDED);
|
|
||||||
|
|
||||||
ASSERT_EQ(calibration.depth_camera_calibration.resolution_width, 0);
|
for (int i = 0; i < 5; i++)
|
||||||
ASSERT_EQ(calibration.depth_camera_calibration.resolution_width, 0);
|
{
|
||||||
|
k4a_depth_mode_t depth_mode = K4A_DEPTH_MODE_OFF;
|
||||||
|
k4a_color_resolution_t color_resolution = K4A_COLOR_RESOLUTION_OFF;
|
||||||
|
|
||||||
|
switch (i)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
depth_mode = K4A_DEPTH_MODE_NFOV_UNBINNED;
|
||||||
|
color_resolution = K4A_COLOR_RESOLUTION_OFF;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
depth_mode = K4A_DEPTH_MODE_OFF;
|
||||||
|
color_resolution = K4A_COLOR_RESOLUTION_720P;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
depth_mode = K4A_DEPTH_MODE_NFOV_2X2BINNED;
|
||||||
|
color_resolution = K4A_COLOR_RESOLUTION_720P;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
depth_mode = K4A_DEPTH_MODE_NFOV_UNBINNED;
|
||||||
|
color_resolution = K4A_COLOR_RESOLUTION_2160P;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
depth_mode = K4A_DEPTH_MODE_NFOV_UNBINNED;
|
||||||
|
color_resolution = K4A_COLOR_RESOLUTION_720P;
|
||||||
|
}
|
||||||
|
|
||||||
|
k4a_calibration_t calibration;
|
||||||
|
k4a_result_t result =
|
||||||
|
k4a_calibration_get_from_raw(g_test_json, sizeof(g_test_json), depth_mode, color_resolution, &calibration);
|
||||||
|
ASSERT_EQ(result, K4A_RESULT_SUCCEEDED);
|
||||||
|
|
||||||
|
k4a_transformation_t transformation_handle = transformation_create(&calibration, false);
|
||||||
|
ASSERT_NE(transformation_handle, (k4a_transformation_t)NULL);
|
||||||
|
|
||||||
|
k4a_result_t result_color_to_depth =
|
||||||
|
transformation_color_image_to_depth_camera(transformation_handle,
|
||||||
|
depth_image_buffer,
|
||||||
|
&depth_image_descriptor,
|
||||||
|
color_image_buffer,
|
||||||
|
&color_image_descriptor,
|
||||||
|
transformed_color_image_buffer,
|
||||||
|
&transformed_color_image_descriptor);
|
||||||
|
|
||||||
|
k4a_result_t result_depth_to_color =
|
||||||
|
transformation_depth_image_to_color_camera(transformation_handle,
|
||||||
|
depth_image_buffer,
|
||||||
|
&depth_image_descriptor,
|
||||||
|
transformed_depth_image_buffer,
|
||||||
|
&transformed_depth_image_descriptor);
|
||||||
|
|
||||||
|
k4a_result_t result_xyz_depth = transformation_depth_image_to_point_cloud(transformation_handle,
|
||||||
|
depth_image_buffer,
|
||||||
|
&depth_image_descriptor,
|
||||||
|
K4A_CALIBRATION_TYPE_DEPTH,
|
||||||
|
xyz_depth_image_buffer,
|
||||||
|
&xyz_depth_image_descriptor);
|
||||||
|
|
||||||
|
k4a_result_t result_xyz_color = transformation_depth_image_to_point_cloud(transformation_handle,
|
||||||
|
transformed_depth_image_buffer,
|
||||||
|
&transformed_depth_image_descriptor,
|
||||||
|
K4A_CALIBRATION_TYPE_COLOR,
|
||||||
|
xyz_color_image_buffer,
|
||||||
|
&xyz_color_image_descriptor);
|
||||||
|
|
||||||
|
if (i != 4)
|
||||||
|
{
|
||||||
|
ASSERT_NE(result_color_to_depth, K4A_RESULT_SUCCEEDED);
|
||||||
|
ASSERT_NE(result_depth_to_color, K4A_RESULT_SUCCEEDED);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ASSERT_EQ(result_color_to_depth, K4A_RESULT_SUCCEEDED);
|
||||||
|
ASSERT_EQ(result_depth_to_color, K4A_RESULT_SUCCEEDED);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i != 0 && i != 3 && i != 4)
|
||||||
|
{
|
||||||
|
ASSERT_NE(result_xyz_depth, K4A_RESULT_SUCCEEDED);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ASSERT_EQ(result_xyz_depth, K4A_RESULT_SUCCEEDED);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i != 1 && i != 2 && i != 4)
|
||||||
|
{
|
||||||
|
ASSERT_NE(result_xyz_color, K4A_RESULT_SUCCEEDED);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ASSERT_EQ(result_xyz_color, K4A_RESULT_SUCCEEDED);
|
||||||
|
}
|
||||||
|
|
||||||
|
transformation_destroy(transformation_handle);
|
||||||
|
}
|
||||||
|
|
||||||
image_dec_ref(depth_image);
|
image_dec_ref(depth_image);
|
||||||
image_dec_ref(color_image);
|
image_dec_ref(color_image);
|
||||||
image_dec_ref(transformed_color_image);
|
image_dec_ref(transformed_color_image);
|
||||||
image_dec_ref(transformed_depth_image);
|
image_dec_ref(transformed_depth_image);
|
||||||
transformation_destroy(transformation_handle);
|
image_dec_ref(xyz_color_image);
|
||||||
|
image_dec_ref(xyz_depth_image);
|
||||||
image_dec_ref(color_image);
|
|
||||||
image_dec_ref(depth_image);
|
|
||||||
image_dec_ref(transformed_color_image);
|
|
||||||
image_dec_ref(transformed_depth_image);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
# Licensed under the MIT License.
|
||||||
|
|
||||||
|
add_executable(logging_ut logging_ut.cpp)
|
||||||
|
|
||||||
|
target_link_libraries(logging_ut PRIVATE
|
||||||
|
k4ainternal::utcommon
|
||||||
|
|
||||||
|
# Link k4ainternal::logging without transitive dependencies
|
||||||
|
$<TARGET_FILE:k4ainternal::logging>
|
||||||
|
# Link the dependencies of k4ainternal::logging that we do not mock
|
||||||
|
)
|
||||||
|
|
||||||
|
# Include the PUBLIC and INTERFACE directories specified by k4ainternal::logging
|
||||||
|
target_include_directories(logging_ut PRIVATE $<TARGET_PROPERTY:k4ainternal::logging,INTERFACE_INCLUDE_DIRECTORIES>)
|
||||||
|
|
||||||
|
k4a_add_tests(TARGET logging_ut TEST_TYPE UNIT)
|
|
@ -0,0 +1,302 @@
|
||||||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
#include <utcommon.h>
|
||||||
|
|
||||||
|
// Module being tested
|
||||||
|
#include <k4ainternal/common.h>
|
||||||
|
#include <k4ainternal/logging.h>
|
||||||
|
#include <azure_c_shared_utility/lock.h>
|
||||||
|
#include <azure_c_shared_utility/tickcounter.h>
|
||||||
|
#include <azure_c_shared_utility/threadapi.h>
|
||||||
|
|
||||||
|
using namespace testing;
|
||||||
|
|
||||||
|
class logging_ut : public ::testing::Test
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
void SetUp() override {}
|
||||||
|
|
||||||
|
void TearDown() override {}
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(logging_ut, create)
|
||||||
|
{
|
||||||
|
// Create the logging instance
|
||||||
|
logger_t logger_handle1 = nullptr;
|
||||||
|
logger_t logger_handle2 = nullptr;
|
||||||
|
logger_config_t config;
|
||||||
|
|
||||||
|
logger_config_init_default(&config);
|
||||||
|
|
||||||
|
// Validate input checking
|
||||||
|
ASSERT_EQ(K4A_RESULT_FAILED, logger_create(nullptr, nullptr));
|
||||||
|
ASSERT_EQ(K4A_RESULT_FAILED, logger_create(&config, nullptr));
|
||||||
|
ASSERT_EQ(K4A_RESULT_FAILED, logger_create(nullptr, &logger_handle1));
|
||||||
|
ASSERT_EQ(logger_handle1, (logger_t) nullptr);
|
||||||
|
|
||||||
|
// Create an instance
|
||||||
|
ASSERT_EQ(K4A_RESULT_SUCCEEDED, logger_create(&config, &logger_handle1));
|
||||||
|
ASSERT_NE(logger_handle1, (logger_t) nullptr);
|
||||||
|
|
||||||
|
// Create a second instance
|
||||||
|
ASSERT_EQ(K4A_RESULT_SUCCEEDED, logger_create(&config, &logger_handle2));
|
||||||
|
ASSERT_NE(logger_handle2, (logger_t) nullptr);
|
||||||
|
ASSERT_NE(logger_handle2, logger_handle1);
|
||||||
|
|
||||||
|
// Verify the instances are unique
|
||||||
|
ASSERT_NE(logger_handle1, logger_handle2);
|
||||||
|
|
||||||
|
LOG_TRACE("Test Trace Message", 0);
|
||||||
|
LOG_INFO("Test Info Message", 0);
|
||||||
|
LOG_WARNING("Test Warning Message", 0);
|
||||||
|
LOG_ERROR("Test Error Message", 0);
|
||||||
|
LOG_CRITICAL("Test Critical Message", 0);
|
||||||
|
|
||||||
|
logger_destroy(logger_handle1);
|
||||||
|
logger_destroy(logger_handle2);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct _logger_test_callback_info_t
|
||||||
|
{
|
||||||
|
int message_count_trace;
|
||||||
|
int message_count_info;
|
||||||
|
int message_count_warning;
|
||||||
|
int message_count_error;
|
||||||
|
int message_count_critical;
|
||||||
|
} logger_test_callback_info_t;
|
||||||
|
|
||||||
|
k4a_logging_message_cb_t logging_callback_function;
|
||||||
|
k4a_logging_message_cb_t logging_callback_function_not_used;
|
||||||
|
|
||||||
|
void logging_callback_function(void *context, k4a_log_level_t level, const char *file, int line, const char *message)
|
||||||
|
{
|
||||||
|
ASSERT_TRUE(level >= K4A_LOG_LEVEL_CRITICAL && level <= K4A_LOG_LEVEL_TRACE);
|
||||||
|
ASSERT_TRUE(context != nullptr);
|
||||||
|
|
||||||
|
(void)file;
|
||||||
|
(void)line;
|
||||||
|
(void)message;
|
||||||
|
|
||||||
|
logger_test_callback_info_t *info = (logger_test_callback_info_t *)context;
|
||||||
|
switch (level)
|
||||||
|
{
|
||||||
|
case K4A_LOG_LEVEL_CRITICAL:
|
||||||
|
info->message_count_critical++;
|
||||||
|
break;
|
||||||
|
case K4A_LOG_LEVEL_ERROR:
|
||||||
|
info->message_count_error++;
|
||||||
|
break;
|
||||||
|
case K4A_LOG_LEVEL_WARNING:
|
||||||
|
info->message_count_warning++;
|
||||||
|
break;
|
||||||
|
case K4A_LOG_LEVEL_INFO:
|
||||||
|
info->message_count_info++;
|
||||||
|
break;
|
||||||
|
case K4A_LOG_LEVEL_TRACE:
|
||||||
|
default:
|
||||||
|
info->message_count_trace++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void logging_callback_function_not_used(void *context,
|
||||||
|
k4a_log_level_t level,
|
||||||
|
const char *file,
|
||||||
|
int line,
|
||||||
|
const char *message)
|
||||||
|
{
|
||||||
|
ASSERT_TRUE(0);
|
||||||
|
|
||||||
|
(void)context;
|
||||||
|
(void)level;
|
||||||
|
(void)file;
|
||||||
|
(void)line;
|
||||||
|
(void)message;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(logging_ut, callback)
|
||||||
|
{
|
||||||
|
logger_test_callback_info_t info = { 0 };
|
||||||
|
|
||||||
|
// Validate input checking
|
||||||
|
ASSERT_EQ(K4A_RESULT_SUCCEEDED, logger_register_message_callback(nullptr, nullptr, (k4a_log_level_t)-1));
|
||||||
|
ASSERT_EQ(K4A_RESULT_FAILED,
|
||||||
|
logger_register_message_callback(logging_callback_function, &info, (k4a_log_level_t)-1));
|
||||||
|
ASSERT_EQ(K4A_RESULT_FAILED,
|
||||||
|
logger_register_message_callback(logging_callback_function, &info, (k4a_log_level_t)100));
|
||||||
|
|
||||||
|
// successful register with no context
|
||||||
|
ASSERT_EQ(K4A_RESULT_SUCCEEDED,
|
||||||
|
logger_register_message_callback(logging_callback_function, nullptr, K4A_LOG_LEVEL_TRACE));
|
||||||
|
// successful unregister
|
||||||
|
ASSERT_EQ(K4A_RESULT_SUCCEEDED, logger_register_message_callback(nullptr, nullptr, (k4a_log_level_t)-1));
|
||||||
|
// successful register
|
||||||
|
ASSERT_EQ(K4A_RESULT_SUCCEEDED,
|
||||||
|
logger_register_message_callback(logging_callback_function, &info, K4A_LOG_LEVEL_TRACE));
|
||||||
|
|
||||||
|
// 2nd registration should fail
|
||||||
|
ASSERT_EQ(K4A_RESULT_FAILED,
|
||||||
|
logger_register_message_callback(logging_callback_function_not_used, &info, K4A_LOG_LEVEL_INFO));
|
||||||
|
|
||||||
|
{
|
||||||
|
memset(&info, 0, sizeof(info));
|
||||||
|
LOG_TRACE("Test Trace Message", 0);
|
||||||
|
LOG_INFO("Test Info Message", 0);
|
||||||
|
LOG_WARNING("Test Warning Message", 0);
|
||||||
|
LOG_ERROR("Test Error Message", 0);
|
||||||
|
LOG_CRITICAL("Test Critical Message", 0);
|
||||||
|
|
||||||
|
ASSERT_EQ(info.message_count_critical, 1);
|
||||||
|
ASSERT_EQ(info.message_count_error, 1);
|
||||||
|
ASSERT_EQ(info.message_count_warning, 1);
|
||||||
|
ASSERT_EQ(info.message_count_info, 1);
|
||||||
|
ASSERT_EQ(info.message_count_trace, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// re-registration (same function ptr) should pass and update level
|
||||||
|
ASSERT_EQ(K4A_RESULT_SUCCEEDED,
|
||||||
|
logger_register_message_callback(logging_callback_function, &info, K4A_LOG_LEVEL_ERROR));
|
||||||
|
|
||||||
|
{
|
||||||
|
memset(&info, 0, sizeof(info));
|
||||||
|
LOG_TRACE("Test Trace Message", 0);
|
||||||
|
LOG_INFO("Test Info Message", 0);
|
||||||
|
LOG_WARNING("Test Warning Message", 0);
|
||||||
|
LOG_ERROR("Test Error Message", 0);
|
||||||
|
LOG_CRITICAL("Test Critical Message", 0);
|
||||||
|
|
||||||
|
ASSERT_EQ(info.message_count_critical, 1);
|
||||||
|
ASSERT_EQ(info.message_count_error, 1);
|
||||||
|
ASSERT_EQ(info.message_count_warning, 0);
|
||||||
|
ASSERT_EQ(info.message_count_info, 0);
|
||||||
|
ASSERT_EQ(info.message_count_trace, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// re-registration (same function ptr) should pass and update level
|
||||||
|
ASSERT_EQ(K4A_RESULT_SUCCEEDED,
|
||||||
|
logger_register_message_callback(logging_callback_function, &info, K4A_LOG_LEVEL_OFF));
|
||||||
|
|
||||||
|
{
|
||||||
|
memset(&info, 0, sizeof(info));
|
||||||
|
LOG_TRACE("Test Trace Message", 0);
|
||||||
|
LOG_INFO("Test Info Message", 0);
|
||||||
|
LOG_WARNING("Test Warning Message", 0);
|
||||||
|
LOG_ERROR("Test Error Message", 0);
|
||||||
|
LOG_CRITICAL("Test Critical Message", 0);
|
||||||
|
|
||||||
|
ASSERT_EQ(info.message_count_critical, 0);
|
||||||
|
ASSERT_EQ(info.message_count_error, 0);
|
||||||
|
ASSERT_EQ(info.message_count_warning, 0);
|
||||||
|
ASSERT_EQ(info.message_count_info, 0);
|
||||||
|
ASSERT_EQ(info.message_count_trace, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// multiple calls to clear the callback function
|
||||||
|
ASSERT_EQ(K4A_RESULT_SUCCEEDED, logger_register_message_callback(nullptr, &info, K4A_LOG_LEVEL_ERROR));
|
||||||
|
ASSERT_EQ(K4A_RESULT_SUCCEEDED, logger_register_message_callback(nullptr, &info, K4A_LOG_LEVEL_ERROR));
|
||||||
|
|
||||||
|
// registration should pass
|
||||||
|
ASSERT_EQ(K4A_RESULT_SUCCEEDED,
|
||||||
|
logger_register_message_callback(logging_callback_function, &info, K4A_LOG_LEVEL_INFO));
|
||||||
|
|
||||||
|
{
|
||||||
|
memset(&info, 0, sizeof(info));
|
||||||
|
LOG_TRACE("Test Trace Message", 0);
|
||||||
|
LOG_INFO("Test Info Message", 0);
|
||||||
|
LOG_WARNING("Test Warning Message", 0);
|
||||||
|
LOG_ERROR("Test Error Message", 0);
|
||||||
|
LOG_CRITICAL("Test Critical Message", 0);
|
||||||
|
|
||||||
|
ASSERT_EQ(info.message_count_critical, 1);
|
||||||
|
ASSERT_EQ(info.message_count_error, 1);
|
||||||
|
ASSERT_EQ(info.message_count_warning, 1);
|
||||||
|
ASSERT_EQ(info.message_count_info, 1);
|
||||||
|
ASSERT_EQ(info.message_count_trace, 0);
|
||||||
|
}
|
||||||
|
ASSERT_EQ(K4A_RESULT_FAILED,
|
||||||
|
logger_register_message_callback(logging_callback_function_not_used, &info, K4A_LOG_LEVEL_ERROR));
|
||||||
|
ASSERT_EQ(K4A_RESULT_SUCCEEDED, logger_register_message_callback(NULL, NULL, K4A_LOG_LEVEL_ERROR));
|
||||||
|
}
|
||||||
|
|
||||||
|
#define TEST_RETURN_VALUE (22)
|
||||||
|
|
||||||
|
typedef struct _logger_callback_threading_test_data_t
|
||||||
|
{
|
||||||
|
volatile int done;
|
||||||
|
LOCK_HANDLE lock;
|
||||||
|
} logger_callback_threading_test_data_t;
|
||||||
|
|
||||||
|
static int logger_callback_thread(void *param)
|
||||||
|
{
|
||||||
|
logger_callback_threading_test_data_t *data = (logger_callback_threading_test_data_t *)param;
|
||||||
|
|
||||||
|
Lock(data->lock);
|
||||||
|
do
|
||||||
|
{
|
||||||
|
LOG_TRACE("Test Trace Message", 0);
|
||||||
|
LOG_INFO("Test Info Message", 0);
|
||||||
|
LOG_WARNING("Test Warning Message", 0);
|
||||||
|
LOG_ERROR("Test Error Message", 0);
|
||||||
|
LOG_CRITICAL("Test Critical Message", 0);
|
||||||
|
} while (data->done == 0);
|
||||||
|
return TEST_RETURN_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(logging_ut, callback_threading)
|
||||||
|
{
|
||||||
|
THREAD_HANDLE th;
|
||||||
|
TICK_COUNTER_HANDLE tick;
|
||||||
|
tickcounter_ms_t start_time_ms, now;
|
||||||
|
logger_callback_threading_test_data_t data = { 0 };
|
||||||
|
logger_test_callback_info_t info = { 0 };
|
||||||
|
int count = 1;
|
||||||
|
|
||||||
|
ASSERT_NE((data.lock = Lock_Init()), (LOCK_HANDLE)NULL);
|
||||||
|
|
||||||
|
ASSERT_NE((TICK_COUNTER_HANDLE)NULL, tick = tickcounter_create());
|
||||||
|
|
||||||
|
// prevent the threads from running
|
||||||
|
Lock(data.lock);
|
||||||
|
|
||||||
|
ASSERT_EQ(THREADAPI_OK, ThreadAPI_Create(&th, logger_callback_thread, &data));
|
||||||
|
|
||||||
|
ThreadAPI_Sleep(100); // Let the thread get started.
|
||||||
|
|
||||||
|
// start the test
|
||||||
|
Unlock(data.lock);
|
||||||
|
|
||||||
|
ASSERT_EQ(0, tickcounter_get_current_ms(tick, &start_time_ms));
|
||||||
|
do
|
||||||
|
{
|
||||||
|
// Loop registering and unregistering a callback function while another thread continuous writes messages.
|
||||||
|
ThreadAPI_Sleep(20);
|
||||||
|
ASSERT_EQ(K4A_RESULT_SUCCEEDED,
|
||||||
|
logger_register_message_callback(logging_callback_function, &info, K4A_LOG_LEVEL_TRACE))
|
||||||
|
<< " the count is " << count << "\n";
|
||||||
|
|
||||||
|
ThreadAPI_Sleep(20);
|
||||||
|
ASSERT_EQ(K4A_RESULT_SUCCEEDED, logger_register_message_callback(NULL, NULL, K4A_LOG_LEVEL_TRACE))
|
||||||
|
<< " the count is " << count << "\n";
|
||||||
|
|
||||||
|
ASSERT_EQ(0, tickcounter_get_current_ms(tick, &now));
|
||||||
|
count++;
|
||||||
|
} while (now - start_time_ms < 5000);
|
||||||
|
|
||||||
|
data.done = true;
|
||||||
|
|
||||||
|
// Wait for the thread to terminate
|
||||||
|
int result;
|
||||||
|
ASSERT_EQ(THREADAPI_OK, ThreadAPI_Join(th, &result));
|
||||||
|
|
||||||
|
ASSERT_EQ(result, TEST_RETURN_VALUE);
|
||||||
|
|
||||||
|
// Verify all our allocations were released
|
||||||
|
Lock_Deinit(data.lock);
|
||||||
|
tickcounter_destroy(tick);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
return k4a_test_commmon_main(argc, argv);
|
||||||
|
}
|
|
@ -11,9 +11,10 @@ set(SOURCE_FILES
|
||||||
k4adevicedockcontrol.cpp
|
k4adevicedockcontrol.cpp
|
||||||
k4afilepicker.cpp
|
k4afilepicker.cpp
|
||||||
k4aimguiextensions.cpp
|
k4aimguiextensions.cpp
|
||||||
k4aimudatagraph.cpp
|
k4aimugraph.cpp
|
||||||
k4aimusamplesource.cpp
|
k4aimugraphdatagenerator.cpp
|
||||||
k4aimuwindow.cpp
|
k4aimuwindow.cpp
|
||||||
|
k4alogdockcontrol.cpp
|
||||||
k4amicrophone.cpp
|
k4amicrophone.cpp
|
||||||
k4amicrophonelistener.cpp
|
k4amicrophonelistener.cpp
|
||||||
k4apointcloudrenderer.cpp
|
k4apointcloudrenderer.cpp
|
||||||
|
@ -26,6 +27,7 @@ set(SOURCE_FILES
|
||||||
k4avideowindow.cpp
|
k4avideowindow.cpp
|
||||||
k4aviewer.cpp
|
k4aviewer.cpp
|
||||||
k4aviewerimage.cpp
|
k4aviewerimage.cpp
|
||||||
|
k4aviewerlogmanager.cpp
|
||||||
k4aviewererrormanager.cpp
|
k4aviewererrormanager.cpp
|
||||||
k4aviewersettingsmanager.cpp
|
k4aviewersettingsmanager.cpp
|
||||||
k4awindowmanager.cpp
|
k4awindowmanager.cpp
|
||||||
|
@ -46,7 +48,6 @@ configure_file(
|
||||||
)
|
)
|
||||||
|
|
||||||
find_package(OpenGL REQUIRED)
|
find_package(OpenGL REQUIRED)
|
||||||
find_package(LibUSB REQUIRED)
|
|
||||||
include_directories(
|
include_directories(
|
||||||
${OPENGL_INCLUDE_DIRS}
|
${OPENGL_INCLUDE_DIRS}
|
||||||
${CMAKE_CURRENT_LIST_DIR}
|
${CMAKE_CURRENT_LIST_DIR}
|
||||||
|
|
|
@ -22,6 +22,7 @@ template<typename NotificationType> class IK4AObserver
|
||||||
public:
|
public:
|
||||||
virtual void NotifyData(const NotificationType &data) = 0;
|
virtual void NotifyData(const NotificationType &data) = 0;
|
||||||
virtual void NotifyTermination() = 0;
|
virtual void NotifyTermination() = 0;
|
||||||
|
virtual void ClearData() = 0;
|
||||||
|
|
||||||
virtual ~IK4AObserver() = default;
|
virtual ~IK4AObserver() = default;
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,6 @@
|
||||||
#pragma clang diagnostic push
|
#pragma clang diagnostic push
|
||||||
#pragma clang diagnostic ignored "-Wdocumentation"
|
#pragma clang diagnostic ignored "-Wdocumentation"
|
||||||
#pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
|
#pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
|
||||||
#pragma clang diagnostic ignored "-Wdouble-promotion"
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "turbojpeg.h"
|
#include "turbojpeg.h"
|
||||||
|
|
|
@ -79,6 +79,14 @@ public:
|
||||||
m_failed = true;
|
m_failed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClearData() override
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
|
||||||
|
m_textureBuffers.Clear();
|
||||||
|
m_inputImageBuffer.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
~K4AConvertingImageSourceImpl() override
|
~K4AConvertingImageSourceImpl() override
|
||||||
{
|
{
|
||||||
m_workerThreadShouldExit = true;
|
m_workerThreadShouldExit = true;
|
||||||
|
|
|
@ -71,8 +71,8 @@ public:
|
||||||
void NotifyTermination()
|
void NotifyTermination()
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(m_mutex);
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
|
||||||
m_primed = false;
|
m_primed = false;
|
||||||
|
m_mostRecentData = T();
|
||||||
for (const auto &wpObserver : m_observers)
|
for (const auto &wpObserver : m_observers)
|
||||||
{
|
{
|
||||||
if (auto spObserver = wpObserver.lock())
|
if (auto spObserver = wpObserver.lock())
|
||||||
|
@ -84,6 +84,28 @@ public:
|
||||||
m_observers.clear();
|
m_observers.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClearData()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
m_primed = false;
|
||||||
|
m_mostRecentData = T();
|
||||||
|
for (auto wpObserver = m_observers.begin(); wpObserver != m_observers.end();)
|
||||||
|
{
|
||||||
|
auto spObserver = wpObserver->lock();
|
||||||
|
if (spObserver)
|
||||||
|
{
|
||||||
|
spObserver->ClearData();
|
||||||
|
++wpObserver;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto toDelete = wpObserver;
|
||||||
|
++wpObserver;
|
||||||
|
m_observers.erase(toDelete);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::list<std::weak_ptr<IK4AObserver<T>>> m_observers;
|
std::list<std::weak_ptr<IK4AObserver<T>>> m_observers;
|
||||||
|
|
||||||
|
|
|
@ -31,20 +31,41 @@ namespace
|
||||||
constexpr std::chrono::milliseconds CameraPollingTimeout(2000);
|
constexpr std::chrono::milliseconds CameraPollingTimeout(2000);
|
||||||
constexpr std::chrono::milliseconds ImuPollingTimeout(2000);
|
constexpr std::chrono::milliseconds ImuPollingTimeout(2000);
|
||||||
|
|
||||||
|
constexpr std::chrono::minutes SubordinateModeStartupTimeout(5);
|
||||||
|
|
||||||
|
constexpr std::chrono::milliseconds PollingThreadCleanShutdownTimeout = std::chrono::milliseconds(1000 / 5);
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void StopSensor(k4a::device *device,
|
||||||
|
std::function<void(k4a::device *)> stopFn,
|
||||||
|
K4ADataSource<T> *dataSource,
|
||||||
|
bool *started)
|
||||||
|
{
|
||||||
|
if (*started)
|
||||||
|
{
|
||||||
|
stopFn(device);
|
||||||
|
}
|
||||||
|
dataSource->NotifyTermination();
|
||||||
|
*started = false;
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
bool PollSensor(const char *sensorFriendlyName,
|
bool PollSensor(const char *sensorFriendlyName,
|
||||||
k4a::device *device,
|
k4a::device *device,
|
||||||
K4ADataSource<T> *dataSource,
|
K4ADataSource<T> *dataSource,
|
||||||
bool *paused,
|
bool *paused,
|
||||||
std::function<bool(k4a::device *, T *)> pollFn,
|
bool *started,
|
||||||
std::function<void(k4a::device *)> stopFn)
|
bool *abortInProgress,
|
||||||
|
std::function<bool(k4a::device *, T *, std::chrono::milliseconds)> pollFn,
|
||||||
|
std::function<void(k4a::device *)> stopFn,
|
||||||
|
std::chrono::milliseconds timeout)
|
||||||
{
|
{
|
||||||
bool failureWasTimeout = false;
|
std::string errorMessage;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
T data;
|
T data;
|
||||||
const bool succeeded = pollFn(device, &data);
|
const bool succeeded = pollFn(device, &data, timeout);
|
||||||
if (succeeded)
|
if (succeeded)
|
||||||
{
|
{
|
||||||
if (!*paused)
|
if (!*paused)
|
||||||
|
@ -54,31 +75,59 @@ bool PollSensor(const char *sensorFriendlyName,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We've timed out.
|
errorMessage = "timed out!";
|
||||||
//
|
|
||||||
failureWasTimeout = true;
|
|
||||||
}
|
}
|
||||||
catch (const k4a::error &)
|
catch (const k4a::error &e)
|
||||||
{
|
{
|
||||||
failureWasTimeout = false;
|
errorMessage = e.what();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::stringstream errorBuilder;
|
StopSensor(device, stopFn, dataSource, started);
|
||||||
errorBuilder << sensorFriendlyName;
|
|
||||||
if (failureWasTimeout)
|
if (!*abortInProgress)
|
||||||
{
|
{
|
||||||
errorBuilder << " timed out!";
|
std::stringstream errorBuilder;
|
||||||
}
|
errorBuilder << sensorFriendlyName << " failed: " << errorMessage;
|
||||||
else
|
K4AViewerErrorManager::Instance().SetErrorStatus(errorBuilder.str());
|
||||||
{
|
|
||||||
errorBuilder << " failed!";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
K4AViewerErrorManager::Instance().SetErrorStatus(errorBuilder.str());
|
|
||||||
dataSource->NotifyTermination();
|
|
||||||
stopFn(device);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void StopPollingThread(std::unique_ptr<K4APollingThread> *pollingThread,
|
||||||
|
k4a::device *device,
|
||||||
|
std::function<void(k4a::device *)> stopFn,
|
||||||
|
K4ADataSource<T> *dataSource,
|
||||||
|
bool *started,
|
||||||
|
bool *abortInProgress)
|
||||||
|
{
|
||||||
|
*abortInProgress = true;
|
||||||
|
if (*pollingThread)
|
||||||
|
{
|
||||||
|
(*pollingThread)->StopAsync();
|
||||||
|
|
||||||
|
// Attempt graceful shutdown of the polling thread to reduce noise.
|
||||||
|
// If this doesn't work out, we'll stop the device manually, which will
|
||||||
|
// make the polling thread's blocking call to get the next data sample abort.
|
||||||
|
//
|
||||||
|
auto startTime = std::chrono::high_resolution_clock::now();
|
||||||
|
while (*started)
|
||||||
|
{
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||||
|
auto now = std::chrono::high_resolution_clock::now();
|
||||||
|
if (now - startTime > PollingThreadCleanShutdownTimeout)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StopSensor(device, stopFn, dataSource, started);
|
||||||
|
pollingThread->reset();
|
||||||
|
*abortInProgress = false;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void K4ADeviceDockControl::ShowColorControl(k4a_color_control_command_t command,
|
void K4ADeviceDockControl::ShowColorControl(k4a_color_control_command_t command,
|
||||||
|
@ -155,9 +204,6 @@ void K4ADeviceDockControl::ApplyDefaultColorSettings()
|
||||||
m_colorSettingsCache.WhiteBalance = { K4A_COLOR_CONTROL_MODE_AUTO, 4500 };
|
m_colorSettingsCache.WhiteBalance = { K4A_COLOR_CONTROL_MODE_AUTO, 4500 };
|
||||||
ApplyColorSetting(K4A_COLOR_CONTROL_WHITEBALANCE, &m_colorSettingsCache.WhiteBalance);
|
ApplyColorSetting(K4A_COLOR_CONTROL_WHITEBALANCE, &m_colorSettingsCache.WhiteBalance);
|
||||||
|
|
||||||
m_colorSettingsCache.AutoExposurePriority = { K4A_COLOR_CONTROL_MODE_MANUAL, 1 };
|
|
||||||
ApplyColorSetting(K4A_COLOR_CONTROL_AUTO_EXPOSURE_PRIORITY, &m_colorSettingsCache.AutoExposurePriority);
|
|
||||||
|
|
||||||
m_colorSettingsCache.Brightness = { K4A_COLOR_CONTROL_MODE_MANUAL, 128 };
|
m_colorSettingsCache.Brightness = { K4A_COLOR_CONTROL_MODE_MANUAL, 128 };
|
||||||
ApplyColorSetting(K4A_COLOR_CONTROL_BRIGHTNESS, &m_colorSettingsCache.Brightness);
|
ApplyColorSetting(K4A_COLOR_CONTROL_BRIGHTNESS, &m_colorSettingsCache.Brightness);
|
||||||
|
|
||||||
|
@ -196,12 +242,11 @@ void K4ADeviceDockControl::LoadColorSettingsCache()
|
||||||
{
|
{
|
||||||
// If more color controls are added, they need to be initialized here
|
// If more color controls are added, they need to be initialized here
|
||||||
//
|
//
|
||||||
static_assert(sizeof(m_colorSettingsCache) == sizeof(ColorSetting) * 10,
|
static_assert(sizeof(m_colorSettingsCache) == sizeof(ColorSetting) * 9,
|
||||||
"Missing color setting in LoadColorSettingsCache()");
|
"Missing color setting in LoadColorSettingsCache()");
|
||||||
|
|
||||||
ReadColorSetting(K4A_COLOR_CONTROL_EXPOSURE_TIME_ABSOLUTE, &m_colorSettingsCache.ExposureTimeUs);
|
ReadColorSetting(K4A_COLOR_CONTROL_EXPOSURE_TIME_ABSOLUTE, &m_colorSettingsCache.ExposureTimeUs);
|
||||||
ReadColorSetting(K4A_COLOR_CONTROL_WHITEBALANCE, &m_colorSettingsCache.WhiteBalance);
|
ReadColorSetting(K4A_COLOR_CONTROL_WHITEBALANCE, &m_colorSettingsCache.WhiteBalance);
|
||||||
ReadColorSetting(K4A_COLOR_CONTROL_AUTO_EXPOSURE_PRIORITY, &m_colorSettingsCache.AutoExposurePriority);
|
|
||||||
ReadColorSetting(K4A_COLOR_CONTROL_BRIGHTNESS, &m_colorSettingsCache.Brightness);
|
ReadColorSetting(K4A_COLOR_CONTROL_BRIGHTNESS, &m_colorSettingsCache.Brightness);
|
||||||
ReadColorSetting(K4A_COLOR_CONTROL_CONTRAST, &m_colorSettingsCache.Contrast);
|
ReadColorSetting(K4A_COLOR_CONTROL_CONTRAST, &m_colorSettingsCache.Contrast);
|
||||||
ReadColorSetting(K4A_COLOR_CONTROL_SATURATION, &m_colorSettingsCache.Saturation);
|
ReadColorSetting(K4A_COLOR_CONTROL_SATURATION, &m_colorSettingsCache.Saturation);
|
||||||
|
@ -523,13 +568,6 @@ K4ADockControlStatus K4ADeviceDockControl::Show()
|
||||||
|
|
||||||
ImGui::PopItemWidth();
|
ImGui::PopItemWidth();
|
||||||
|
|
||||||
ShowColorControl(K4A_COLOR_CONTROL_AUTO_EXPOSURE_PRIORITY, &m_colorSettingsCache.AutoExposurePriority,
|
|
||||||
[](ColorSetting *cacheEntry) {
|
|
||||||
return ImGui::Checkbox("Auto Exposure Priority", reinterpret_cast<bool *>(&cacheEntry->Value)) ?
|
|
||||||
ColorControlAction::SetManual :
|
|
||||||
ColorControlAction::None;
|
|
||||||
});
|
|
||||||
|
|
||||||
ShowColorControl(K4A_COLOR_CONTROL_BACKLIGHT_COMPENSATION, &m_colorSettingsCache.BacklightCompensation,
|
ShowColorControl(K4A_COLOR_CONTROL_BACKLIGHT_COMPENSATION, &m_colorSettingsCache.BacklightCompensation,
|
||||||
[](ColorSetting *cacheEntry) {
|
[](ColorSetting *cacheEntry) {
|
||||||
return ImGui::Checkbox("Backlight Compensation", reinterpret_cast<bool *>(&cacheEntry->Value)) ?
|
return ImGui::Checkbox("Backlight Compensation", reinterpret_cast<bool *>(&cacheEntry->Value)) ?
|
||||||
|
@ -755,6 +793,16 @@ K4ADockControlStatus K4ADeviceDockControl::Show()
|
||||||
{
|
{
|
||||||
ImGuiExtensions::ButtonColorChanger colorChanger(ImGuiExtensions::ButtonColor::Green);
|
ImGuiExtensions::ButtonColorChanger colorChanger(ImGuiExtensions::ButtonColor::Green);
|
||||||
const bool validStartMode = enableCameras || m_config.EnableMicrophone || m_config.EnableImu;
|
const bool validStartMode = enableCameras || m_config.EnableMicrophone || m_config.EnableImu;
|
||||||
|
|
||||||
|
if (m_config.WiredSyncMode == K4A_WIRED_SYNC_MODE_SUBORDINATE)
|
||||||
|
{
|
||||||
|
ImGuiExtensions::TextColorChanger cc(ImGuiExtensions::TextColor::Warning);
|
||||||
|
ImGui::TextUnformatted("You are starting in subordinate mode.");
|
||||||
|
ImGui::TextUnformatted("The camera will not start until it");
|
||||||
|
ImGui::TextUnformatted("receives a start signal from the");
|
||||||
|
ImGui::TextUnformatted("master device");
|
||||||
|
}
|
||||||
|
|
||||||
if (ImGuiExtensions::K4AButton("Start", buttonSize, validStartMode))
|
if (ImGuiExtensions::K4AButton("Start", buttonSize, validStartMode))
|
||||||
{
|
{
|
||||||
Start();
|
Start();
|
||||||
|
@ -805,11 +853,11 @@ void K4ADeviceDockControl::Start()
|
||||||
const bool enableCameras = m_config.EnableColorCamera || m_config.EnableDepthCamera;
|
const bool enableCameras = m_config.EnableColorCamera || m_config.EnableDepthCamera;
|
||||||
if (enableCameras)
|
if (enableCameras)
|
||||||
{
|
{
|
||||||
StartCameras();
|
bool camerasStarted = StartCameras();
|
||||||
}
|
if (camerasStarted && m_config.EnableImu)
|
||||||
if (m_config.EnableImu)
|
{
|
||||||
{
|
StartImu();
|
||||||
StartImu();
|
}
|
||||||
}
|
}
|
||||||
if (m_config.EnableMicrophone)
|
if (m_config.EnableMicrophone)
|
||||||
{
|
{
|
||||||
|
@ -855,20 +903,32 @@ bool K4ADeviceDockControl::StartCameras()
|
||||||
K4ADataSource<k4a::capture> *pCameraDataSource = &m_cameraDataSource;
|
K4ADataSource<k4a::capture> *pCameraDataSource = &m_cameraDataSource;
|
||||||
bool *pPaused = &m_paused;
|
bool *pPaused = &m_paused;
|
||||||
bool *pCamerasStarted = &m_camerasStarted;
|
bool *pCamerasStarted = &m_camerasStarted;
|
||||||
|
bool *pAbortInProgress = &m_camerasAbortInProgress;
|
||||||
|
bool isSubordinate = m_config.WiredSyncMode == K4A_WIRED_SYNC_MODE_SUBORDINATE;
|
||||||
|
|
||||||
m_cameraPollingThread = std14::make_unique<K4APollingThread>(
|
m_cameraPollingThread = std14::make_unique<K4APollingThread>(
|
||||||
[pDevice, pCameraDataSource, pPaused, pCamerasStarted]() {
|
[pDevice, pCameraDataSource, pPaused, pCamerasStarted, pAbortInProgress, isSubordinate](bool firstRun) {
|
||||||
|
std::chrono::milliseconds pollingTimeout = CameraPollingTimeout;
|
||||||
|
if (firstRun && isSubordinate)
|
||||||
|
{
|
||||||
|
// If we're starting in subordinate mode, we need to give the user time to start the
|
||||||
|
// master device, so we wait for longer.
|
||||||
|
//
|
||||||
|
pollingTimeout = SubordinateModeStartupTimeout;
|
||||||
|
}
|
||||||
return PollSensor<k4a::capture>("Cameras",
|
return PollSensor<k4a::capture>("Cameras",
|
||||||
pDevice,
|
pDevice,
|
||||||
pCameraDataSource,
|
pCameraDataSource,
|
||||||
pPaused,
|
pPaused,
|
||||||
[](k4a::device *device, k4a::capture *capture) {
|
pCamerasStarted,
|
||||||
return device->get_capture(capture, CameraPollingTimeout);
|
pAbortInProgress,
|
||||||
|
[](k4a::device *device,
|
||||||
|
k4a::capture *capture,
|
||||||
|
std::chrono::milliseconds timeout) {
|
||||||
|
return device->get_capture(capture, timeout);
|
||||||
},
|
},
|
||||||
[pCamerasStarted](k4a::device *device) {
|
[](k4a::device *device) { device->stop_cameras(); },
|
||||||
device->stop_cameras();
|
pollingTimeout);
|
||||||
*pCamerasStarted = false;
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -876,13 +936,12 @@ bool K4ADeviceDockControl::StartCameras()
|
||||||
|
|
||||||
void K4ADeviceDockControl::StopCameras()
|
void K4ADeviceDockControl::StopCameras()
|
||||||
{
|
{
|
||||||
if (m_cameraPollingThread)
|
StopPollingThread(&m_cameraPollingThread,
|
||||||
{
|
&m_device,
|
||||||
m_cameraPollingThread.reset();
|
[](k4a::device *device) { device->stop_cameras(); },
|
||||||
}
|
&m_cameraDataSource,
|
||||||
m_cameraDataSource.NotifyTermination();
|
&m_camerasStarted,
|
||||||
m_device.stop_cameras();
|
&m_camerasAbortInProgress);
|
||||||
m_camerasStarted = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool K4ADeviceDockControl::StartMicrophone()
|
bool K4ADeviceDockControl::StartMicrophone()
|
||||||
|
@ -943,34 +1002,45 @@ bool K4ADeviceDockControl::StartImu()
|
||||||
K4ADataSource<k4a_imu_sample_t> *pImuDataSource = &m_imuDataSource;
|
K4ADataSource<k4a_imu_sample_t> *pImuDataSource = &m_imuDataSource;
|
||||||
bool *pPaused = &m_paused;
|
bool *pPaused = &m_paused;
|
||||||
bool *pImuStarted = &m_imuStarted;
|
bool *pImuStarted = &m_imuStarted;
|
||||||
|
bool *pAbortInProgress = &m_imuAbortInProgress;
|
||||||
|
bool isSubordinate = m_config.WiredSyncMode == K4A_WIRED_SYNC_MODE_SUBORDINATE;
|
||||||
|
|
||||||
m_imuPollingThread = std14::make_unique<K4APollingThread>([pDevice, pImuDataSource, pPaused, pImuStarted]() {
|
m_imuPollingThread = std14::make_unique<K4APollingThread>(
|
||||||
return PollSensor<k4a_imu_sample_t>("IMU",
|
[pDevice, pImuDataSource, pPaused, pImuStarted, pAbortInProgress, isSubordinate](bool firstRun) {
|
||||||
pDevice,
|
std::chrono::milliseconds pollingTimeout = ImuPollingTimeout;
|
||||||
pImuDataSource,
|
if (firstRun && isSubordinate)
|
||||||
pPaused,
|
{
|
||||||
[](k4a::device *device, k4a_imu_sample_t *sample) {
|
// If we're starting in subordinate mode, we need to give the user time to start the
|
||||||
return device->get_imu_sample(sample, ImuPollingTimeout);
|
// master device, so we wait for longer.
|
||||||
},
|
//
|
||||||
[pImuStarted](k4a::device *device) {
|
pollingTimeout = SubordinateModeStartupTimeout;
|
||||||
device->stop_imu();
|
}
|
||||||
*pImuStarted = false;
|
return PollSensor<k4a_imu_sample_t>("IMU",
|
||||||
});
|
pDevice,
|
||||||
});
|
pImuDataSource,
|
||||||
|
pPaused,
|
||||||
|
pImuStarted,
|
||||||
|
pAbortInProgress,
|
||||||
|
[](k4a::device *device,
|
||||||
|
k4a_imu_sample_t *sample,
|
||||||
|
std::chrono::milliseconds timeout) {
|
||||||
|
return device->get_imu_sample(sample, timeout);
|
||||||
|
},
|
||||||
|
[](k4a::device *device) { device->stop_imu(); },
|
||||||
|
pollingTimeout);
|
||||||
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void K4ADeviceDockControl::StopImu()
|
void K4ADeviceDockControl::StopImu()
|
||||||
{
|
{
|
||||||
if (m_imuPollingThread)
|
StopPollingThread(&m_imuPollingThread,
|
||||||
{
|
&m_device,
|
||||||
m_imuPollingThread.reset();
|
[](k4a::device *device) { device->stop_imu(); },
|
||||||
}
|
&m_imuDataSource,
|
||||||
m_imuDataSource.NotifyTermination();
|
&m_imuStarted,
|
||||||
m_imuStarted = false;
|
&m_imuAbortInProgress);
|
||||||
|
|
||||||
m_device.stop_imu();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void K4ADeviceDockControl::SetViewType(K4AWindowSet::ViewType viewType)
|
void K4ADeviceDockControl::SetViewType(K4AWindowSet::ViewType viewType)
|
||||||
|
|
|
@ -47,7 +47,6 @@ private:
|
||||||
{
|
{
|
||||||
ColorSetting ExposureTimeUs;
|
ColorSetting ExposureTimeUs;
|
||||||
ColorSetting WhiteBalance;
|
ColorSetting WhiteBalance;
|
||||||
ColorSetting AutoExposurePriority;
|
|
||||||
ColorSetting Brightness;
|
ColorSetting Brightness;
|
||||||
ColorSetting Contrast;
|
ColorSetting Contrast;
|
||||||
ColorSetting Saturation;
|
ColorSetting Saturation;
|
||||||
|
@ -107,6 +106,9 @@ private:
|
||||||
bool m_camerasStarted = false;
|
bool m_camerasStarted = false;
|
||||||
bool m_imuStarted = false;
|
bool m_imuStarted = false;
|
||||||
|
|
||||||
|
bool m_camerasAbortInProgress = false;
|
||||||
|
bool m_imuAbortInProgress = false;
|
||||||
|
|
||||||
std::string m_deviceSerialNumber;
|
std::string m_deviceSerialNumber;
|
||||||
std::shared_ptr<K4AMicrophone> m_microphone;
|
std::shared_ptr<K4AMicrophone> m_microphone;
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ public:
|
||||||
template<k4a_image_format_t T> static k4a::image GetImageFromCapture(const k4a::capture &capture)
|
template<k4a_image_format_t T> static k4a::image GetImageFromCapture(const k4a::capture &capture)
|
||||||
{
|
{
|
||||||
k4a::image img = capture.get_color_image();
|
k4a::image img = capture.get_color_image();
|
||||||
if (img.get_format() != T)
|
if (!img || img.get_format() != T)
|
||||||
{
|
{
|
||||||
return k4a::image();
|
return k4a::image();
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,6 @@
|
||||||
#pragma clang diagnostic push
|
#pragma clang diagnostic push
|
||||||
#pragma clang diagnostic ignored "-Wdocumentation"
|
#pragma clang diagnostic ignored "-Wdocumentation"
|
||||||
#pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
|
#pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
|
||||||
#pragma clang diagnostic ignored "-Wdouble-promotion"
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <GL/gl3w.h>
|
#include <GL/gl3w.h>
|
||||||
|
|
|
@ -1,74 +0,0 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License.
|
|
||||||
|
|
||||||
#ifndef K4AIMUDATAGRAPH_H
|
|
||||||
#define K4AIMUDATAGRAPH_H
|
|
||||||
|
|
||||||
// System headers
|
|
||||||
//
|
|
||||||
#include <array>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
// Library headers
|
|
||||||
//
|
|
||||||
#include <k4a/k4a.hpp>
|
|
||||||
#include "k4aimgui_all.h"
|
|
||||||
|
|
||||||
// Project headers
|
|
||||||
//
|
|
||||||
|
|
||||||
namespace k4aviewer
|
|
||||||
{
|
|
||||||
class K4AImuDataGraph
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
K4AImuDataGraph(std::string &&title,
|
|
||||||
std::string &&xLabel,
|
|
||||||
std::string &&yLabel,
|
|
||||||
std::string &&zLabel,
|
|
||||||
std::string &&units,
|
|
||||||
float minRange,
|
|
||||||
float maxRange,
|
|
||||||
float defaultRange,
|
|
||||||
float scaleFactor);
|
|
||||||
|
|
||||||
void AddSample(const k4a_float3_t &sample, uint64_t timestampUs);
|
|
||||||
void Show(ImVec2 maxSize);
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Number of samples to show in the graphs
|
|
||||||
//
|
|
||||||
static constexpr int GraphSampleCount = 150;
|
|
||||||
|
|
||||||
// Number of data samples we average together to compute a graph sample
|
|
||||||
//
|
|
||||||
static constexpr int DataSamplesPerGraphSample = 20;
|
|
||||||
|
|
||||||
void PlotGraph(const char *name, const std::array<float, GraphSampleCount> &data, ImVec2 graphSize);
|
|
||||||
|
|
||||||
const std::string m_title;
|
|
||||||
const std::string m_xLabel;
|
|
||||||
const std::string m_yLabel;
|
|
||||||
const std::string m_zLabel;
|
|
||||||
const std::string m_units;
|
|
||||||
|
|
||||||
const float m_minRange;
|
|
||||||
const float m_maxRange;
|
|
||||||
float m_currentRange;
|
|
||||||
|
|
||||||
const float m_scaleFactor;
|
|
||||||
|
|
||||||
uint64_t m_lastTimestamp = 0;
|
|
||||||
size_t m_offset = 0;
|
|
||||||
std::array<float, GraphSampleCount> m_x = {};
|
|
||||||
std::array<float, GraphSampleCount> m_y = {};
|
|
||||||
std::array<float, GraphSampleCount> m_z = {};
|
|
||||||
|
|
||||||
k4a_float3_t m_nextSampleAccumulator{ { 0.f, 0.f, 0.f } };
|
|
||||||
int m_nextSampleAccumulatorCount = 0;
|
|
||||||
|
|
||||||
const std::string m_scaleTitle;
|
|
||||||
};
|
|
||||||
} // namespace k4aviewer
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
// Associated header
|
// Associated header
|
||||||
//
|
//
|
||||||
#include "k4aimudatagraph.h"
|
#include "k4aimugraph.h"
|
||||||
|
|
||||||
// System headers
|
// System headers
|
||||||
//
|
//
|
||||||
|
@ -41,15 +41,14 @@ constexpr float MinHeight = 50.f;
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
K4AImuDataGraph::K4AImuDataGraph(std::string &&title,
|
K4AImuGraph::K4AImuGraph(std::string &&title,
|
||||||
std::string &&xLabel,
|
std::string &&xLabel,
|
||||||
std::string &&yLabel,
|
std::string &&yLabel,
|
||||||
std::string &&zLabel,
|
std::string &&zLabel,
|
||||||
std::string &&units,
|
std::string &&units,
|
||||||
const float minRange,
|
const float minRange,
|
||||||
const float maxRange,
|
const float maxRange,
|
||||||
const float defaultRange,
|
const float defaultRange) :
|
||||||
const float scaleFactor) :
|
|
||||||
m_title(std::move(title)),
|
m_title(std::move(title)),
|
||||||
m_xLabel(std::move(xLabel)),
|
m_xLabel(std::move(xLabel)),
|
||||||
m_yLabel(std::move(yLabel)),
|
m_yLabel(std::move(yLabel)),
|
||||||
|
@ -58,38 +57,14 @@ K4AImuDataGraph::K4AImuDataGraph(std::string &&title,
|
||||||
m_minRange(minRange),
|
m_minRange(minRange),
|
||||||
m_maxRange(maxRange),
|
m_maxRange(maxRange),
|
||||||
m_currentRange(-defaultRange),
|
m_currentRange(-defaultRange),
|
||||||
m_scaleFactor(scaleFactor),
|
|
||||||
m_scaleTitle(GetScaleTitle(m_title))
|
m_scaleTitle(GetScaleTitle(m_title))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void K4AImuDataGraph::AddSample(const k4a_float3_t &sample, const uint64_t timestampUs)
|
void K4AImuGraph::Show(ImVec2 maxSize,
|
||||||
{
|
const K4AImuGraphData::AccumulatorArray &graphData,
|
||||||
// We want to average a few samples together to slow down the graph enough that it's readable
|
int graphFrontIdx,
|
||||||
//
|
uint64_t timestamp)
|
||||||
m_nextSampleAccumulator.xyz.x += sample.xyz.x;
|
|
||||||
m_nextSampleAccumulator.xyz.y += sample.xyz.y;
|
|
||||||
m_nextSampleAccumulator.xyz.z += sample.xyz.z;
|
|
||||||
++m_nextSampleAccumulatorCount;
|
|
||||||
|
|
||||||
if (m_nextSampleAccumulatorCount >= DataSamplesPerGraphSample)
|
|
||||||
{
|
|
||||||
m_offset = (m_offset + 1) % m_x.size();
|
|
||||||
|
|
||||||
m_x[m_offset] = m_nextSampleAccumulator.xyz.x / m_nextSampleAccumulatorCount * m_scaleFactor;
|
|
||||||
m_y[m_offset] = m_nextSampleAccumulator.xyz.y / m_nextSampleAccumulatorCount * m_scaleFactor;
|
|
||||||
m_z[m_offset] = m_nextSampleAccumulator.xyz.z / m_nextSampleAccumulatorCount * m_scaleFactor;
|
|
||||||
|
|
||||||
m_lastTimestamp = timestampUs;
|
|
||||||
|
|
||||||
m_nextSampleAccumulator.xyz.x = 0.f;
|
|
||||||
m_nextSampleAccumulator.xyz.y = 0.f;
|
|
||||||
m_nextSampleAccumulator.xyz.z = 0.f;
|
|
||||||
m_nextSampleAccumulatorCount = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void K4AImuDataGraph::Show(ImVec2 maxSize)
|
|
||||||
{
|
{
|
||||||
// One line for the graph type (accelerometer/gyro), one for the timestamp
|
// One line for the graph type (accelerometer/gyro), one for the timestamp
|
||||||
//
|
//
|
||||||
|
@ -116,7 +91,7 @@ void K4AImuDataGraph::Show(ImVec2 maxSize)
|
||||||
|
|
||||||
ImGui::BeginGroup();
|
ImGui::BeginGroup();
|
||||||
ImGui::Text("%s", m_title.c_str());
|
ImGui::Text("%s", m_title.c_str());
|
||||||
ImGui::Text("Time (us): %llu", static_cast<long long unsigned int>(m_lastTimestamp));
|
ImGui::Text("Time (us): %llu", static_cast<long long unsigned int>(timestamp));
|
||||||
|
|
||||||
// We use negative min/max ranges to reverse the direction of the slider, which makes it
|
// We use negative min/max ranges to reverse the direction of the slider, which makes it
|
||||||
// grow when you drag up, which is a bit more intuitive.
|
// grow when you drag up, which is a bit more intuitive.
|
||||||
|
@ -130,22 +105,26 @@ void K4AImuDataGraph::Show(ImVec2 maxSize)
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
ImGui::BeginGroup();
|
ImGui::BeginGroup();
|
||||||
PlotGraph(m_xLabel.c_str(), m_x, graphSize);
|
PlotGraph(m_xLabel.c_str(), graphSize, graphData, graphFrontIdx, 0);
|
||||||
PlotGraph(m_yLabel.c_str(), m_y, graphSize);
|
PlotGraph(m_yLabel.c_str(), graphSize, graphData, graphFrontIdx, 1);
|
||||||
PlotGraph(m_zLabel.c_str(), m_z, graphSize);
|
PlotGraph(m_zLabel.c_str(), graphSize, graphData, graphFrontIdx, 2);
|
||||||
ImGui::EndGroup();
|
ImGui::EndGroup();
|
||||||
ImGui::EndGroup();
|
ImGui::EndGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
void K4AImuDataGraph::PlotGraph(const char *name, const std::array<float, GraphSampleCount> &data, ImVec2 graphSize)
|
void K4AImuGraph::PlotGraph(const char *name,
|
||||||
|
ImVec2 graphSize,
|
||||||
|
const K4AImuGraphData::AccumulatorArray &graphData,
|
||||||
|
int graphFrontIdx,
|
||||||
|
int offset)
|
||||||
{
|
{
|
||||||
std::stringstream nameBuilder;
|
std::stringstream nameBuilder;
|
||||||
nameBuilder << "##" << name;
|
nameBuilder << "##" << name;
|
||||||
|
|
||||||
const float currentData = data[m_offset];
|
const float currentData = graphData[static_cast<size_t>(graphFrontIdx)].v[offset];
|
||||||
|
|
||||||
std::string label;
|
std::string label;
|
||||||
if (K4AViewerSettingsManager::Instance().GetShowInfoPane())
|
if (K4AViewerSettingsManager::Instance().GetViewerOption(ViewerOption::ShowInfoPane))
|
||||||
{
|
{
|
||||||
std::stringstream labelBuilder;
|
std::stringstream labelBuilder;
|
||||||
labelBuilder << name << ": ";
|
labelBuilder << name << ": ";
|
||||||
|
@ -175,10 +154,23 @@ void K4AImuDataGraph::PlotGraph(const char *name, const std::array<float, GraphS
|
||||||
label = labelBuilder.str();
|
label = labelBuilder.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct SelectorData
|
||||||
|
{
|
||||||
|
const k4a_float3_t *data;
|
||||||
|
int offset;
|
||||||
|
} selectorData;
|
||||||
|
|
||||||
|
selectorData.data = &graphData[0];
|
||||||
|
selectorData.offset = offset;
|
||||||
|
|
||||||
ImGui::PlotLines(nameBuilder.str().c_str(),
|
ImGui::PlotLines(nameBuilder.str().c_str(),
|
||||||
&data[0],
|
[](void *data, int idx) {
|
||||||
static_cast<int>(data.size()),
|
auto *sd = reinterpret_cast<SelectorData *>(data);
|
||||||
static_cast<int>(m_offset),
|
return sd->data[idx].v[sd->offset];
|
||||||
|
},
|
||||||
|
&selectorData,
|
||||||
|
static_cast<int>(graphData.size()),
|
||||||
|
graphFrontIdx,
|
||||||
label.c_str(),
|
label.c_str(),
|
||||||
m_currentRange,
|
m_currentRange,
|
||||||
-m_currentRange,
|
-m_currentRange,
|
|
@ -0,0 +1,60 @@
|
||||||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
#ifndef K4AIMUGRAPH_H
|
||||||
|
#define K4AIMUGRAPH_H
|
||||||
|
|
||||||
|
// System headers
|
||||||
|
//
|
||||||
|
#include <array>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// Library headers
|
||||||
|
//
|
||||||
|
#include <k4a/k4a.hpp>
|
||||||
|
#include "k4aimgui_all.h"
|
||||||
|
|
||||||
|
// Project headers
|
||||||
|
//
|
||||||
|
#include "k4aimugraphdatagenerator.h"
|
||||||
|
|
||||||
|
namespace k4aviewer
|
||||||
|
{
|
||||||
|
|
||||||
|
class K4AImuGraph
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
K4AImuGraph(std::string &&title,
|
||||||
|
std::string &&xLabel,
|
||||||
|
std::string &&yLabel,
|
||||||
|
std::string &&zLabel,
|
||||||
|
std::string &&units,
|
||||||
|
float minRange,
|
||||||
|
float maxRange,
|
||||||
|
float defaultRange);
|
||||||
|
|
||||||
|
void
|
||||||
|
Show(ImVec2 maxSize, const K4AImuGraphData::AccumulatorArray &graphData, int graphFrontIdx, uint64_t timestamp);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void PlotGraph(const char *name,
|
||||||
|
ImVec2 graphSize,
|
||||||
|
const K4AImuGraphData::AccumulatorArray &graphData,
|
||||||
|
int graphFrontIdx,
|
||||||
|
int offset);
|
||||||
|
|
||||||
|
const std::string m_title;
|
||||||
|
const std::string m_xLabel;
|
||||||
|
const std::string m_yLabel;
|
||||||
|
const std::string m_zLabel;
|
||||||
|
const std::string m_units;
|
||||||
|
|
||||||
|
const float m_minRange;
|
||||||
|
const float m_maxRange;
|
||||||
|
float m_currentRange;
|
||||||
|
|
||||||
|
const std::string m_scaleTitle;
|
||||||
|
};
|
||||||
|
} // namespace k4aviewer
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,83 @@
|
||||||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
// Associated header
|
||||||
|
//
|
||||||
|
#include "k4aimugraphdatagenerator.h"
|
||||||
|
|
||||||
|
// System headers
|
||||||
|
//
|
||||||
|
|
||||||
|
// Library headers
|
||||||
|
//
|
||||||
|
|
||||||
|
// Project headers
|
||||||
|
//
|
||||||
|
|
||||||
|
using namespace k4aviewer;
|
||||||
|
|
||||||
|
void K4AImuGraphDataGenerator::NotifyData(const k4a_imu_sample_t &sample)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; ++i)
|
||||||
|
{
|
||||||
|
m_accAccumulator.v[i] += sample.acc_sample.v[i];
|
||||||
|
m_gyroAccumulator.v[i] += sample.gyro_sample.v[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
++m_accumulatorCount;
|
||||||
|
|
||||||
|
// 'Commit' the samples we've accumulated to the graph
|
||||||
|
//
|
||||||
|
if (m_accumulatorCount >= SamplesPerAggregateSample)
|
||||||
|
{
|
||||||
|
size_t insertOffset = static_cast<size_t>(m_graphData.StartOffset);
|
||||||
|
m_graphData.StartOffset = (m_graphData.StartOffset + 1) % K4AImuGraphData::GraphSampleCount;
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; ++i)
|
||||||
|
{
|
||||||
|
m_graphData.AccData[insertOffset].v[i] = m_accAccumulator.v[i] / SamplesPerAggregateSample;
|
||||||
|
m_graphData.GyroData[insertOffset].v[i] = m_gyroAccumulator.v[i] / SamplesPerAggregateSample;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_graphData.AccTimestamp = sample.acc_timestamp_usec;
|
||||||
|
m_graphData.GyroTimestamp = sample.gyro_timestamp_usec;
|
||||||
|
m_graphData.LastTemperature = sample.temperature;
|
||||||
|
|
||||||
|
ResetAccumulators();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void K4AImuGraphDataGenerator::NotifyTermination()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
m_failed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void K4AImuGraphDataGenerator::ClearData()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
ResetAccumulators();
|
||||||
|
std::fill(m_graphData.AccData.begin(), m_graphData.AccData.end(), k4a_float3_t{ { 0.f, 0.f, 0.f } });
|
||||||
|
std::fill(m_graphData.GyroData.begin(), m_graphData.GyroData.end(), k4a_float3_t{ { 0.f, 0.f, 0.f } });
|
||||||
|
|
||||||
|
m_graphData.AccTimestamp = 0;
|
||||||
|
m_graphData.GyroTimestamp = 0;
|
||||||
|
|
||||||
|
m_graphData.StartOffset = 0;
|
||||||
|
m_graphData.LastTemperature = std::numeric_limits<float>::quiet_NaN();
|
||||||
|
}
|
||||||
|
|
||||||
|
K4AImuGraphDataGenerator::K4AImuGraphDataGenerator()
|
||||||
|
{
|
||||||
|
ClearData();
|
||||||
|
ResetAccumulators();
|
||||||
|
}
|
||||||
|
|
||||||
|
void K4AImuGraphDataGenerator::ResetAccumulators()
|
||||||
|
{
|
||||||
|
m_gyroAccumulator = { { 0.f, 0.f, 0.f } };
|
||||||
|
m_accAccumulator = { { 0.f, 0.f, 0.f } };
|
||||||
|
m_accumulatorCount = 0;
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
#ifndef K4AIMUGRAPHDATAGENERATOR_H
|
||||||
|
#define K4AIMUGRAPHDATAGENERATOR_H
|
||||||
|
|
||||||
|
// System headers
|
||||||
|
//
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
// Library headers
|
||||||
|
//
|
||||||
|
#include <k4a/k4a.hpp>
|
||||||
|
|
||||||
|
// Project headers
|
||||||
|
//
|
||||||
|
#include "ik4aobserver.h"
|
||||||
|
#include "k4aringbuffer.h"
|
||||||
|
|
||||||
|
namespace k4aviewer
|
||||||
|
{
|
||||||
|
|
||||||
|
struct K4AImuGraphData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static constexpr int GraphSampleCount = 150;
|
||||||
|
|
||||||
|
using AccumulatorArray = std::array<k4a_float3_t, GraphSampleCount>;
|
||||||
|
|
||||||
|
AccumulatorArray AccData;
|
||||||
|
AccumulatorArray GyroData;
|
||||||
|
|
||||||
|
uint64_t AccTimestamp;
|
||||||
|
uint64_t GyroTimestamp;
|
||||||
|
|
||||||
|
float LastTemperature;
|
||||||
|
|
||||||
|
int StartOffset;
|
||||||
|
};
|
||||||
|
|
||||||
|
class K4AImuGraphDataGenerator : public IK4AImuObserver
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void NotifyData(const k4a_imu_sample_t &sample) override;
|
||||||
|
void NotifyTermination() override;
|
||||||
|
void ClearData() override;
|
||||||
|
|
||||||
|
struct GraphReader
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> Lock;
|
||||||
|
const K4AImuGraphData *Data;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Returns a pointer-to-graph-data and a lock that guarantees
|
||||||
|
// that the graph data won't be modified. You must release the lock
|
||||||
|
// in a timely fashion or the graph generator will hang.
|
||||||
|
//
|
||||||
|
GraphReader GetGraphData() const
|
||||||
|
{
|
||||||
|
return GraphReader{ std::unique_lock<std::mutex>(m_mutex), &m_graphData };
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsFailed() const
|
||||||
|
{
|
||||||
|
return m_failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
K4AImuGraphDataGenerator();
|
||||||
|
~K4AImuGraphDataGenerator() override = default;
|
||||||
|
|
||||||
|
static constexpr int SamplesPerAggregateSample = 20;
|
||||||
|
static constexpr int SamplesPerGraph = SamplesPerAggregateSample * K4AImuGraphData::GraphSampleCount;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void ResetAccumulators();
|
||||||
|
|
||||||
|
K4AImuGraphData m_graphData;
|
||||||
|
|
||||||
|
bool m_failed = false;
|
||||||
|
|
||||||
|
k4a_float3_t m_gyroAccumulator;
|
||||||
|
k4a_float3_t m_accAccumulator;
|
||||||
|
int m_accumulatorCount = 0;
|
||||||
|
|
||||||
|
mutable std::mutex m_mutex;
|
||||||
|
};
|
||||||
|
} // namespace k4aviewer
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,48 +0,0 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License.
|
|
||||||
|
|
||||||
// Associated header
|
|
||||||
//
|
|
||||||
#include "k4aimusamplesource.h"
|
|
||||||
|
|
||||||
// System headers
|
|
||||||
//
|
|
||||||
|
|
||||||
// Library headers
|
|
||||||
//
|
|
||||||
|
|
||||||
// Project headers
|
|
||||||
//
|
|
||||||
|
|
||||||
using namespace k4aviewer;
|
|
||||||
|
|
||||||
void K4AImuSampleSource::NotifyData(const k4a_imu_sample_t &data)
|
|
||||||
{
|
|
||||||
if (!m_sampleBuffer.BeginInsert())
|
|
||||||
{
|
|
||||||
// Buffer overflowed; drop the sample.
|
|
||||||
//
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
*m_sampleBuffer.InsertionItem() = data;
|
|
||||||
|
|
||||||
m_sampleBuffer.EndInsert();
|
|
||||||
}
|
|
||||||
|
|
||||||
void K4AImuSampleSource::NotifyTermination()
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(m_mutex);
|
|
||||||
m_failed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool K4AImuSampleSource::PopSample(k4a_imu_sample_t *out)
|
|
||||||
{
|
|
||||||
if (!m_sampleBuffer.AdvanceRead())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
*out = *m_sampleBuffer.CurrentItem();
|
|
||||||
return true;
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License.
|
|
||||||
|
|
||||||
#ifndef K4AIMUSAMPLESOURCE_H
|
|
||||||
#define K4AIMUSAMPLESOURCE_H
|
|
||||||
|
|
||||||
// System headers
|
|
||||||
//
|
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
// Library headers
|
|
||||||
//
|
|
||||||
#include <k4a/k4a.hpp>
|
|
||||||
|
|
||||||
// Project headers
|
|
||||||
//
|
|
||||||
#include "ik4aobserver.h"
|
|
||||||
#include "k4aringbuffer.h"
|
|
||||||
|
|
||||||
namespace k4aviewer
|
|
||||||
{
|
|
||||||
class K4AImuSampleSource : public IK4AImuObserver
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void NotifyData(const k4a_imu_sample_t &data) override;
|
|
||||||
void NotifyTermination() override;
|
|
||||||
|
|
||||||
bool PopSample(k4a_imu_sample_t *out);
|
|
||||||
|
|
||||||
bool IsFailed() const
|
|
||||||
{
|
|
||||||
return m_failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
~K4AImuSampleSource() override = default;
|
|
||||||
|
|
||||||
private:
|
|
||||||
K4ARingBuffer<k4a_imu_sample_t, 1000> m_sampleBuffer;
|
|
||||||
bool m_failed = false;
|
|
||||||
|
|
||||||
mutable std::mutex m_mutex;
|
|
||||||
};
|
|
||||||
} // namespace k4aviewer
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
// System headers
|
// System headers
|
||||||
//
|
//
|
||||||
|
#include <cmath>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
// Library headers
|
// Library headers
|
||||||
|
@ -15,7 +16,6 @@
|
||||||
|
|
||||||
// Project headers
|
// Project headers
|
||||||
//
|
//
|
||||||
#include "k4aimudatagraph.h"
|
|
||||||
#include "k4aviewererrormanager.h"
|
#include "k4aviewererrormanager.h"
|
||||||
#include "k4awindowsizehelpers.h"
|
#include "k4awindowsizehelpers.h"
|
||||||
|
|
||||||
|
@ -26,59 +26,35 @@ namespace
|
||||||
constexpr float AccelMinRange = 5.0f;
|
constexpr float AccelMinRange = 5.0f;
|
||||||
constexpr float AccelMaxRange = 100.0f;
|
constexpr float AccelMaxRange = 100.0f;
|
||||||
constexpr float AccelDefaultRange = 20.0f;
|
constexpr float AccelDefaultRange = 20.0f;
|
||||||
constexpr float AccelScaleFactor = 1.0f;
|
|
||||||
|
|
||||||
constexpr float GyroMinRange = 5.0f;
|
constexpr float GyroMinRange = 5.0f;
|
||||||
constexpr float GyroMaxRange = 50.0f;
|
constexpr float GyroMaxRange = 50.0f;
|
||||||
constexpr float GyroDefaultRange = 20.0f;
|
constexpr float GyroDefaultRange = 20.0f;
|
||||||
constexpr float GyroScaleFactor = 1.0f;
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
K4AImuWindow::K4AImuWindow(std::string &&title, std::shared_ptr<K4AImuSampleSource> sampleSource) :
|
K4AImuWindow::K4AImuWindow(std::string &&title, std::shared_ptr<K4AImuGraphDataGenerator> graphDataGenerator) :
|
||||||
m_sampleSource(std::move(sampleSource)),
|
m_graphDataGenerator(std::move(graphDataGenerator)),
|
||||||
m_title(std::move(title)),
|
m_title(std::move(title)),
|
||||||
m_accelerometerGraph("Accelerometer",
|
m_accGraph("Accelerometer", "X", "Y", "Z", "m/s/s", AccelMinRange, AccelMaxRange, AccelDefaultRange),
|
||||||
"X",
|
m_gyroGraph("Gyroscope", " Roll", "Pitch", " Yaw", "Rad/s", GyroMinRange, GyroMaxRange, GyroDefaultRange)
|
||||||
"Y",
|
|
||||||
"Z",
|
|
||||||
"m/s/s",
|
|
||||||
AccelMinRange,
|
|
||||||
AccelMaxRange,
|
|
||||||
AccelDefaultRange,
|
|
||||||
AccelScaleFactor),
|
|
||||||
m_gyroscopeGraph("Gyroscope",
|
|
||||||
" Roll",
|
|
||||||
"Pitch",
|
|
||||||
" Yaw",
|
|
||||||
"Rad/s",
|
|
||||||
GyroMinRange,
|
|
||||||
GyroMaxRange,
|
|
||||||
GyroDefaultRange,
|
|
||||||
GyroScaleFactor)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void K4AImuWindow::Show(K4AWindowPlacementInfo placementInfo)
|
void K4AImuWindow::Show(K4AWindowPlacementInfo placementInfo)
|
||||||
{
|
{
|
||||||
if (!m_failed && m_sampleSource->IsFailed())
|
if (!m_failed && m_graphDataGenerator->IsFailed())
|
||||||
{
|
{
|
||||||
K4AViewerErrorManager::Instance().SetErrorStatus(m_title + ": sample source failed!");
|
K4AViewerErrorManager::Instance().SetErrorStatus(m_title + ": data source failed!");
|
||||||
m_failed = true;
|
m_failed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_failed)
|
if (m_failed)
|
||||||
{
|
{
|
||||||
ImGui::Text("Sample source failed!");
|
ImGui::Text("Data source failed!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
k4a_imu_sample_t sample;
|
K4AImuGraphDataGenerator::GraphReader reader = m_graphDataGenerator->GetGraphData();
|
||||||
while (m_sampleSource->PopSample(&sample))
|
|
||||||
{
|
|
||||||
m_accelerometerGraph.AddSample(sample.acc_sample, sample.acc_timestamp_usec);
|
|
||||||
m_gyroscopeGraph.AddSample(sample.gyro_sample, sample.gyro_timestamp_usec);
|
|
||||||
m_sensorTemperature = static_cast<double>(sample.temperature);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sizing math
|
// Sizing math
|
||||||
//
|
//
|
||||||
|
@ -100,14 +76,18 @@ void K4AImuWindow::Show(K4AWindowPlacementInfo placementInfo)
|
||||||
|
|
||||||
// Actually draw the widgets
|
// Actually draw the widgets
|
||||||
//
|
//
|
||||||
m_accelerometerGraph.Show(graphSize);
|
m_accGraph.Show(graphSize, reader.Data->AccData, reader.Data->StartOffset, reader.Data->AccTimestamp);
|
||||||
|
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
|
|
||||||
m_gyroscopeGraph.Show(graphSize);
|
m_gyroGraph.Show(graphSize, reader.Data->GyroData, reader.Data->StartOffset, reader.Data->GyroTimestamp);
|
||||||
|
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
ImGui::Text("Sensor temperature: %.2f C", m_sensorTemperature);
|
|
||||||
|
if (!std::isnan(reader.Data->LastTemperature))
|
||||||
|
{
|
||||||
|
ImGui::Text("Sensor temperature: %.2f C", static_cast<double>(reader.Data->LastTemperature));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *K4AImuWindow::GetTitle() const
|
const char *K4AImuWindow::GetTitle() const
|
||||||
|
|
|
@ -14,9 +14,9 @@
|
||||||
|
|
||||||
// Project headers
|
// Project headers
|
||||||
//
|
//
|
||||||
#include "k4aimusamplesource.h"
|
|
||||||
#include "ik4avisualizationwindow.h"
|
#include "ik4avisualizationwindow.h"
|
||||||
#include "k4aimudatagraph.h"
|
#include "k4aimugraphdatagenerator.h"
|
||||||
|
#include "k4aimugraph.h"
|
||||||
|
|
||||||
namespace k4aviewer
|
namespace k4aviewer
|
||||||
{
|
{
|
||||||
|
@ -27,7 +27,7 @@ public:
|
||||||
|
|
||||||
const char *GetTitle() const override;
|
const char *GetTitle() const override;
|
||||||
|
|
||||||
K4AImuWindow(std::string &&title, std::shared_ptr<K4AImuSampleSource> sampleSource);
|
K4AImuWindow(std::string &&title, std::shared_ptr<K4AImuGraphDataGenerator> graphDataGenerator);
|
||||||
~K4AImuWindow() override = default;
|
~K4AImuWindow() override = default;
|
||||||
|
|
||||||
K4AImuWindow(const K4AImuWindow &) = delete;
|
K4AImuWindow(const K4AImuWindow &) = delete;
|
||||||
|
@ -36,13 +36,13 @@ public:
|
||||||
K4AImuWindow &operator=(const K4AImuWindow &&) = delete;
|
K4AImuWindow &operator=(const K4AImuWindow &&) = delete;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<K4AImuSampleSource> m_sampleSource;
|
std::shared_ptr<K4AImuGraphDataGenerator> m_graphDataGenerator;
|
||||||
|
|
||||||
std::string m_title;
|
std::string m_title;
|
||||||
bool m_failed = false;
|
bool m_failed = false;
|
||||||
|
|
||||||
K4AImuDataGraph m_accelerometerGraph;
|
K4AImuGraph m_accGraph;
|
||||||
K4AImuDataGraph m_gyroscopeGraph;
|
K4AImuGraph m_gyroGraph;
|
||||||
double m_sensorTemperature;
|
|
||||||
};
|
};
|
||||||
} // namespace k4aviewer
|
} // namespace k4aviewer
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,170 @@
|
||||||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
// Associated header
|
||||||
|
//
|
||||||
|
#include "k4alogdockcontrol.h"
|
||||||
|
|
||||||
|
// System headers
|
||||||
|
//
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
// Library headers
|
||||||
|
//
|
||||||
|
|
||||||
|
// Project headers
|
||||||
|
//
|
||||||
|
#include "k4aimguiextensions.h"
|
||||||
|
|
||||||
|
using namespace k4aviewer;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
// Maximum number of log entries to keep in memory
|
||||||
|
//
|
||||||
|
constexpr static size_t MaxLines = 10000;
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
|
||||||
|
// Get a string representation of a log level suitable for printing
|
||||||
|
// in the log box (fields are fixed-width)
|
||||||
|
//
|
||||||
|
const char *LogLevelToString(k4a_log_level_t logLevel)
|
||||||
|
{
|
||||||
|
switch(logLevel)
|
||||||
|
{
|
||||||
|
case K4A_LOG_LEVEL_CRITICAL: return "critical";
|
||||||
|
case K4A_LOG_LEVEL_ERROR: return "error ";
|
||||||
|
case K4A_LOG_LEVEL_WARNING: return "warning ";
|
||||||
|
case K4A_LOG_LEVEL_INFO: return "info ";
|
||||||
|
case K4A_LOG_LEVEL_TRACE: return "trace ";
|
||||||
|
|
||||||
|
default: return "[unknown]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ImVec4 &LogLevelToColor(k4a_log_level_t logLevel)
|
||||||
|
{
|
||||||
|
static const ImVec4 critical = ImVec4(1.f, 0.f, 0.f, 1.f);
|
||||||
|
static const ImVec4 error = ImVec4(1.f, .3f, 0.f, 1.f);
|
||||||
|
static const ImVec4 warning = ImVec4(1.f, 1.f, 0.f, 1.f);
|
||||||
|
static const ImVec4 info = ImVec4(1.f, 1.f, 1.f, 1.f);
|
||||||
|
static const ImVec4 trace = ImVec4(.5f, .5f, .5f, 1.f);
|
||||||
|
|
||||||
|
switch(logLevel)
|
||||||
|
{
|
||||||
|
case K4A_LOG_LEVEL_CRITICAL: return critical;
|
||||||
|
case K4A_LOG_LEVEL_ERROR: return error;
|
||||||
|
case K4A_LOG_LEVEL_WARNING: return warning;
|
||||||
|
case K4A_LOG_LEVEL_INFO: return info;
|
||||||
|
case K4A_LOG_LEVEL_TRACE: return trace;
|
||||||
|
|
||||||
|
default: return warning;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// String mappings used for the combo box used to select error levels
|
||||||
|
//
|
||||||
|
const std::vector<std::pair<k4a_log_level_t, std::string>> LogLevelLabels = {
|
||||||
|
{K4A_LOG_LEVEL_CRITICAL, "Critical"},
|
||||||
|
{K4A_LOG_LEVEL_ERROR, "Error"},
|
||||||
|
{K4A_LOG_LEVEL_WARNING, "Warning"},
|
||||||
|
{K4A_LOG_LEVEL_INFO, "Info"},
|
||||||
|
{K4A_LOG_LEVEL_TRACE, "Trace"}
|
||||||
|
};
|
||||||
|
|
||||||
|
// clang-format on
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
K4ALogDockControl::K4ALogDockControl() : m_logListener(std::make_shared<LogListener>())
|
||||||
|
{
|
||||||
|
K4AViewerLogManager::Instance().RegisterListener(m_logListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
void K4ALogDockControl::LogListener::Log(k4a_log_level_t severity, const char *file, int line, const char *msg)
|
||||||
|
{
|
||||||
|
if (severity > m_minSeverity)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
|
||||||
|
m_entries.emplace_back(severity, file, line, msg);
|
||||||
|
if (m_entries.size() > MaxLines)
|
||||||
|
{
|
||||||
|
m_entries.pop_front();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_updated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
K4ADockControlStatus K4ALogDockControl::Show()
|
||||||
|
{
|
||||||
|
ImGui::BeginGroup();
|
||||||
|
if (ImGui::Button("Clear Log"))
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_logListener->m_mutex);
|
||||||
|
m_logListener->m_entries.clear();
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
const bool copy = ImGui::Button("Copy Log to Clipboard");
|
||||||
|
|
||||||
|
m_logListener->m_updated |= ImGuiExtensions::K4AComboBox("Severity",
|
||||||
|
"",
|
||||||
|
ImGuiComboFlags_None,
|
||||||
|
LogLevelLabels,
|
||||||
|
&m_logListener->m_minSeverity);
|
||||||
|
m_logListener->m_updated |= ImGui::InputText("Search", &m_filterString[0], m_filterString.size());
|
||||||
|
m_logListener->m_updated |= ImGui::Checkbox("Show line info", &m_showLineInfo);
|
||||||
|
ImGui::EndGroup();
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
|
||||||
|
ImGui::BeginChild("LogTextScrollArea", ImVec2(0, 0), false, ImGuiWindowFlags_HorizontalScrollbar);
|
||||||
|
|
||||||
|
if (copy)
|
||||||
|
{
|
||||||
|
ImGui::LogToClipboard();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool updated = false;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_logListener->m_mutex);
|
||||||
|
for (const LogEntry &entry : m_logListener->m_entries)
|
||||||
|
{
|
||||||
|
std::stringstream lineBuilder;
|
||||||
|
lineBuilder << "[ " << LogLevelToString(entry.Severity) << " ] ";
|
||||||
|
|
||||||
|
if (m_showLineInfo)
|
||||||
|
{
|
||||||
|
lineBuilder << "( " << entry.File << ":" << entry.Line << " ) ";
|
||||||
|
}
|
||||||
|
|
||||||
|
lineBuilder << ": " << entry.Msg.c_str();
|
||||||
|
std::string lineStr = lineBuilder.str();
|
||||||
|
if (m_filterString[0] != '\0' && lineStr.find(&m_filterString[0]) == std::string::npos)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ImGui::TextColored(LogLevelToColor(entry.Severity), "%s", lineStr.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
updated = m_logListener->m_updated;
|
||||||
|
m_logListener->m_updated = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (copy)
|
||||||
|
{
|
||||||
|
ImGui::LogFinish();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updated)
|
||||||
|
{
|
||||||
|
ImGui::SetScrollHere(1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndChild();
|
||||||
|
|
||||||
|
return K4ADockControlStatus::Ok;
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
#ifndef K4ALOGDOCKCONTROL_H
|
||||||
|
#define K4ALOGDOCKCONTROL_H
|
||||||
|
|
||||||
|
// System headers
|
||||||
|
//
|
||||||
|
#include <array>
|
||||||
|
#include <list>
|
||||||
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// Library headers
|
||||||
|
//
|
||||||
|
|
||||||
|
// Project headers
|
||||||
|
//
|
||||||
|
#include "ik4adockcontrol.h"
|
||||||
|
#include "k4aviewerlogmanager.h"
|
||||||
|
|
||||||
|
namespace k4aviewer
|
||||||
|
{
|
||||||
|
|
||||||
|
class K4ALogDockControl : public IK4ADockControl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
K4ALogDockControl();
|
||||||
|
~K4ALogDockControl() override = default;
|
||||||
|
|
||||||
|
K4ALogDockControl(K4ALogDockControl &) = delete;
|
||||||
|
K4ALogDockControl(K4ALogDockControl &&) = delete;
|
||||||
|
K4ALogDockControl operator=(K4ALogDockControl &) = delete;
|
||||||
|
K4ALogDockControl operator=(K4ALogDockControl &&) = delete;
|
||||||
|
|
||||||
|
K4ADockControlStatus Show() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct LogEntry
|
||||||
|
{
|
||||||
|
LogEntry() = default;
|
||||||
|
LogEntry(k4a_log_level_t severity, const char *file, int line, const char *msg) :
|
||||||
|
Severity(severity),
|
||||||
|
File(file),
|
||||||
|
Line(line),
|
||||||
|
Msg(msg)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
k4a_log_level_t Severity;
|
||||||
|
std::string File;
|
||||||
|
int Line;
|
||||||
|
std::string Msg;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LogListener : public IK4AViewerLogListener
|
||||||
|
{
|
||||||
|
void Log(k4a_log_level_t severity, const char *file, int line, const char *msg) override;
|
||||||
|
~LogListener() override = default;
|
||||||
|
|
||||||
|
std::list<LogEntry> m_entries;
|
||||||
|
bool m_updated = false;
|
||||||
|
std::mutex m_mutex;
|
||||||
|
k4a_log_level_t m_minSeverity = K4A_LOG_LEVEL_WARNING;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<LogListener> m_logListener;
|
||||||
|
std::array<char, 100> m_filterString = { { '\0' } };
|
||||||
|
bool m_showLineInfo = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace k4aviewer
|
||||||
|
|
||||||
|
#endif
|
|
@ -49,6 +49,12 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClearData() override
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
m_lastCapture.reset();
|
||||||
|
}
|
||||||
|
|
||||||
void NotifyTermination() override
|
void NotifyTermination() override
|
||||||
{
|
{
|
||||||
m_failed = true;
|
m_failed = true;
|
||||||
|
|
|
@ -188,7 +188,11 @@ K4APointCloudVisualizer::K4APointCloudVisualizer(const bool enableColorPointClou
|
||||||
|
|
||||||
m_viewControl.ResetPosition();
|
m_viewControl.ResetPosition();
|
||||||
|
|
||||||
m_colorXyTable = m_pointCloudConverter.GenerateXyTable(m_calibrationData, K4A_CALIBRATION_TYPE_COLOR);
|
if (enableColorPointCloud)
|
||||||
|
{
|
||||||
|
m_colorXyTable = m_pointCloudConverter.GenerateXyTable(m_calibrationData, K4A_CALIBRATION_TYPE_COLOR);
|
||||||
|
}
|
||||||
|
|
||||||
m_depthXyTable = m_pointCloudConverter.GenerateXyTable(m_calibrationData, K4A_CALIBRATION_TYPE_DEPTH);
|
m_depthXyTable = m_pointCloudConverter.GenerateXyTable(m_calibrationData, K4A_CALIBRATION_TYPE_DEPTH);
|
||||||
|
|
||||||
SetColorizationStrategy(m_colorizationStrategy);
|
SetColorizationStrategy(m_colorizationStrategy);
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
//
|
//
|
||||||
#include "k4aimguiextensions.h"
|
#include "k4aimguiextensions.h"
|
||||||
#include "k4aviewererrormanager.h"
|
#include "k4aviewererrormanager.h"
|
||||||
|
#include "k4aviewerlogmanager.h"
|
||||||
#include "k4awindowsizehelpers.h"
|
#include "k4awindowsizehelpers.h"
|
||||||
|
|
||||||
using namespace k4aviewer;
|
using namespace k4aviewer;
|
||||||
|
@ -67,28 +68,6 @@ void K4APointCloudWindow::Show(K4AWindowPlacementInfo placementInfo)
|
||||||
const ImVec2 imageStartPos = ImGui::GetCursorScreenPos();
|
const ImVec2 imageStartPos = ImGui::GetCursorScreenPos();
|
||||||
ImGui::Image(static_cast<ImTextureID>(*m_texture), textureSize);
|
ImGui::Image(static_cast<ImTextureID>(*m_texture), textureSize);
|
||||||
|
|
||||||
if (m_missingColorImages != 0 || m_missingDepthImages != 0)
|
|
||||||
{
|
|
||||||
ImGui::BeginGroup();
|
|
||||||
{
|
|
||||||
ImGuiExtensions::TextColorChanger warningColorChanger(ImGuiExtensions::TextColor::Warning);
|
|
||||||
ImGui::Text("%s", "Warning: some captures were dropped due to missing images!");
|
|
||||||
ImGui::Text("Missing depth images: %d", m_missingDepthImages);
|
|
||||||
ImGui::Text("Missing color images: %d", m_missingColorImages);
|
|
||||||
}
|
|
||||||
ImGui::EndGroup();
|
|
||||||
|
|
||||||
if (!m_haveShownMissingImagesWarning)
|
|
||||||
{
|
|
||||||
std::stringstream warningBuilder;
|
|
||||||
warningBuilder
|
|
||||||
<< "Warning: Some captures didn't have both color and depth data and had to be dropped." << std::endl
|
|
||||||
<< " To avoid this, you can set the \"Synchronized images only\" option under Internal Sync.";
|
|
||||||
K4AViewerErrorManager::Instance().SetErrorStatus(warningBuilder.str());
|
|
||||||
m_haveShownMissingImagesWarning = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int *ipColorizationStrategy = reinterpret_cast<int *>(&m_colorizationStrategy);
|
int *ipColorizationStrategy = reinterpret_cast<int *>(&m_colorizationStrategy);
|
||||||
|
|
||||||
bool colorizationStrategyUpdated = false;
|
bool colorizationStrategyUpdated = false;
|
||||||
|
@ -172,30 +151,48 @@ K4APointCloudWindow::K4APointCloudWindow(std::string &&windowTitle,
|
||||||
|
|
||||||
void K4APointCloudWindow::ProcessInput(ImVec2 imageStartPos, ImVec2 displayDimensions)
|
void K4APointCloudWindow::ProcessInput(ImVec2 imageStartPos, ImVec2 displayDimensions)
|
||||||
{
|
{
|
||||||
if (ImGui::IsWindowFocused())
|
ImGuiIO &io = ImGui::GetIO();
|
||||||
{
|
|
||||||
ImGuiIO &io = ImGui::GetIO();
|
|
||||||
|
|
||||||
const bool leftMouseDown = io.MouseDown[GLFW_MOUSE_BUTTON_1];
|
if (ImGui::IsWindowHovered())
|
||||||
const bool rightMouseDown = io.MouseDown[GLFW_MOUSE_BUTTON_2];
|
{
|
||||||
|
m_pointCloudVisualizer.ProcessMouseScroll(io.MouseWheel);
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseMovementType movementType = MouseMovementType::None;
|
||||||
|
|
||||||
|
bool mouseDown = false;
|
||||||
|
ImVec2 mouseDownPos(-1.f, -1.f);
|
||||||
|
if (io.MouseDown[GLFW_MOUSE_BUTTON_1])
|
||||||
|
{
|
||||||
|
mouseDownPos = io.MouseClickedPos[GLFW_MOUSE_BUTTON_1];
|
||||||
|
movementType = MouseMovementType::Rotation;
|
||||||
|
mouseDown = true;
|
||||||
|
}
|
||||||
|
else if (io.MouseDown[GLFW_MOUSE_BUTTON_2])
|
||||||
|
{
|
||||||
|
mouseDownPos = io.MouseClickedPos[GLFW_MOUSE_BUTTON_2];
|
||||||
|
movementType = MouseMovementType::Translation;
|
||||||
|
mouseDown = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mouseDown)
|
||||||
|
{
|
||||||
|
// Normalize to the image start coordinates
|
||||||
|
//
|
||||||
|
mouseDownPos.x -= imageStartPos.x;
|
||||||
|
mouseDownPos.y -= imageStartPos.y;
|
||||||
|
|
||||||
const linmath::vec2 mousePos{ io.MousePos.x - imageStartPos.x, io.MousePos.y - imageStartPos.y };
|
const linmath::vec2 mousePos{ io.MousePos.x - imageStartPos.x, io.MousePos.y - imageStartPos.y };
|
||||||
|
|
||||||
const linmath::vec2 mouseDelta{ io.MouseDelta.x, io.MouseDelta.y };
|
// Only count drags if they originated on the image
|
||||||
const linmath::vec2 dimensions{ displayDimensions.x, displayDimensions.y };
|
//
|
||||||
|
if (mouseDownPos.x >= 0.f && mouseDownPos.x <= displayDimensions.x && mouseDownPos.y >= 0.f &&
|
||||||
MouseMovementType movementType = MouseMovementType::None;
|
mouseDownPos.y <= displayDimensions.y)
|
||||||
if (leftMouseDown)
|
|
||||||
{
|
{
|
||||||
movementType = MouseMovementType::Rotation;
|
const linmath::vec2 dimensions{ displayDimensions.x, displayDimensions.y };
|
||||||
|
const linmath::vec2 mouseDelta{ io.MouseDelta.x, io.MouseDelta.y };
|
||||||
|
m_pointCloudVisualizer.ProcessMouseMovement(dimensions, mousePos, mouseDelta, movementType);
|
||||||
}
|
}
|
||||||
else if (rightMouseDown)
|
|
||||||
{
|
|
||||||
movementType = MouseMovementType::Translation;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_pointCloudVisualizer.ProcessMouseMovement(dimensions, mousePos, mouseDelta, movementType);
|
|
||||||
m_pointCloudVisualizer.ProcessMouseScroll(io.MouseWheel);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,11 +204,19 @@ bool K4APointCloudWindow::CheckVisualizationResult(PointCloudVisualizationResult
|
||||||
return true;
|
return true;
|
||||||
case PointCloudVisualizationResult::MissingDepthImage:
|
case PointCloudVisualizationResult::MissingDepthImage:
|
||||||
++m_consecutiveMissingImages;
|
++m_consecutiveMissingImages;
|
||||||
++m_missingDepthImages;
|
K4AViewerLogManager::Instance()
|
||||||
|
.Log(K4A_LOG_LEVEL_WARNING,
|
||||||
|
__FILE__,
|
||||||
|
__LINE__,
|
||||||
|
"Dropped a capture due to a missing depth image - set \"Synchronized Images Only\" to avoid this");
|
||||||
break;
|
break;
|
||||||
case PointCloudVisualizationResult::MissingColorImage:
|
case PointCloudVisualizationResult::MissingColorImage:
|
||||||
|
K4AViewerLogManager::Instance()
|
||||||
|
.Log(K4A_LOG_LEVEL_WARNING,
|
||||||
|
__FILE__,
|
||||||
|
__LINE__,
|
||||||
|
"Dropped a capture due to a missing color image - set \"Synchronized Images Only\" to avoid this");
|
||||||
++m_consecutiveMissingImages;
|
++m_consecutiveMissingImages;
|
||||||
++m_missingColorImages;
|
|
||||||
break;
|
break;
|
||||||
case PointCloudVisualizationResult::OpenGlError:
|
case PointCloudVisualizationResult::OpenGlError:
|
||||||
SetFailed("OpenGL error!");
|
SetFailed("OpenGL error!");
|
||||||
|
|
|
@ -57,10 +57,6 @@ private:
|
||||||
|
|
||||||
static constexpr int MaxConsecutiveMissingImages = 10;
|
static constexpr int MaxConsecutiveMissingImages = 10;
|
||||||
int m_consecutiveMissingImages = 0;
|
int m_consecutiveMissingImages = 0;
|
||||||
|
|
||||||
bool m_haveShownMissingImagesWarning = false;
|
|
||||||
int m_missingColorImages = 0;
|
|
||||||
int m_missingDepthImages = 0;
|
|
||||||
};
|
};
|
||||||
} // namespace k4aviewer
|
} // namespace k4aviewer
|
||||||
|
|
||||||
|
|
|
@ -14,26 +14,37 @@
|
||||||
|
|
||||||
// Project headers
|
// Project headers
|
||||||
//
|
//
|
||||||
|
#include "k4aviewerutil.h"
|
||||||
|
|
||||||
namespace k4aviewer
|
namespace k4aviewer
|
||||||
{
|
{
|
||||||
class K4APollingThread
|
class K4APollingThread
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
K4APollingThread(std::function<bool()> &&pollFn) : m_pollFn(std::move(pollFn))
|
K4APollingThread(std::function<bool(bool)> &&pollFn) : m_pollFn(std::move(pollFn))
|
||||||
{
|
{
|
||||||
m_thread = std::thread(&K4APollingThread::Run, this);
|
m_thread = std::thread(&K4APollingThread::Run, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Stop()
|
void Stop()
|
||||||
{
|
{
|
||||||
m_shouldExit = true;
|
StopAsync();
|
||||||
if (m_thread.joinable())
|
if (m_thread.joinable())
|
||||||
{
|
{
|
||||||
m_thread.join();
|
m_thread.join();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void StopAsync()
|
||||||
|
{
|
||||||
|
m_shouldExit = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsRunning() const
|
||||||
|
{
|
||||||
|
return m_isRunning;
|
||||||
|
}
|
||||||
|
|
||||||
~K4APollingThread()
|
~K4APollingThread()
|
||||||
{
|
{
|
||||||
Stop();
|
Stop();
|
||||||
|
@ -42,19 +53,24 @@ public:
|
||||||
private:
|
private:
|
||||||
static void Run(K4APollingThread *instance)
|
static void Run(K4APollingThread *instance)
|
||||||
{
|
{
|
||||||
|
instance->m_isRunning = true;
|
||||||
|
CleanupGuard runGuard([instance]() { instance->m_isRunning = false; });
|
||||||
|
bool firstRun = true;
|
||||||
while (!instance->m_shouldExit)
|
while (!instance->m_shouldExit)
|
||||||
{
|
{
|
||||||
if (!instance->m_pollFn())
|
if (!instance->m_pollFn(firstRun))
|
||||||
{
|
{
|
||||||
instance->m_shouldExit = true;
|
instance->m_shouldExit = true;
|
||||||
}
|
}
|
||||||
|
firstRun = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::thread m_thread;
|
std::thread m_thread;
|
||||||
volatile bool m_shouldExit = false;
|
volatile bool m_shouldExit = false;
|
||||||
|
volatile bool m_isRunning = false;
|
||||||
|
|
||||||
std::function<bool()> m_pollFn;
|
std::function<bool(bool)> m_pollFn;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace k4aviewer
|
} // namespace k4aviewer
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include "k4atypeoperators.h"
|
#include "k4atypeoperators.h"
|
||||||
#include "k4aviewerutil.h"
|
#include "k4aviewerutil.h"
|
||||||
#include "k4awindowmanager.h"
|
#include "k4awindowmanager.h"
|
||||||
|
#include "k4aimugraphdatagenerator.h"
|
||||||
|
|
||||||
using namespace k4aviewer;
|
using namespace k4aviewer;
|
||||||
namespace
|
namespace
|
||||||
|
@ -121,15 +122,15 @@ K4ARecordingDockControl::K4ARecordingDockControl(std::string &&path, k4a::playba
|
||||||
|
|
||||||
m_playbackThreadState.Recording = std::move(recording);
|
m_playbackThreadState.Recording = std::move(recording);
|
||||||
PlaybackThreadState *pThreadState = &m_playbackThreadState;
|
PlaybackThreadState *pThreadState = &m_playbackThreadState;
|
||||||
m_playbackThread = std::unique_ptr<K4APollingThread>(
|
m_playbackThread = std14::make_unique<K4APollingThread>(
|
||||||
new K4APollingThread([pThreadState]() { return PlaybackThreadFn(pThreadState); }));
|
[pThreadState](bool) { return PlaybackThreadFn(pThreadState); });
|
||||||
|
|
||||||
SetViewType(K4AWindowSet::ViewType::Normal);
|
SetViewType(K4AWindowSet::ViewType::Normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
K4ADockControlStatus K4ARecordingDockControl::Show()
|
K4ADockControlStatus K4ARecordingDockControl::Show()
|
||||||
{
|
{
|
||||||
ImGui::Text("%s", m_filenameLabel.c_str());
|
ImGui::TextUnformatted(m_filenameLabel.c_str());
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGuiExtensions::ButtonColorChanger cc(ImGuiExtensions::ButtonColor::Red);
|
ImGuiExtensions::ButtonColorChanger cc(ImGuiExtensions::ButtonColor::Red);
|
||||||
if (ImGui::SmallButton("Close"))
|
if (ImGui::SmallButton("Close"))
|
||||||
|
@ -140,14 +141,15 @@ K4ADockControlStatus K4ARecordingDockControl::Show()
|
||||||
cc.Clear();
|
cc.Clear();
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
|
|
||||||
ImGui::Text("%s", "Image formats");
|
ImGui::TextUnformatted("Recording Settings");
|
||||||
ImGui::Text("FPS: %s", m_fpsLabel.c_str());
|
ImGui::Text("FPS: %s", m_fpsLabel.c_str());
|
||||||
ImGui::Text("Depth mode: %s", m_depthModeLabel.c_str());
|
ImGui::Text("Depth mode: %s", m_depthModeLabel.c_str());
|
||||||
ImGui::Text("Color format: %s", m_colorFormatLabel.c_str());
|
ImGui::Text("Color format: %s", m_colorFormatLabel.c_str());
|
||||||
ImGui::Text("Color resolution: %s", m_colorResolutionLabel.c_str());
|
ImGui::Text("Color resolution: %s", m_colorResolutionLabel.c_str());
|
||||||
|
ImGui::Text("IMU enabled: %s", m_recordConfiguration.imu_track_enabled ? "Yes" : "No");
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
|
|
||||||
ImGui::Text("%s", "Sync settings");
|
ImGui::TextUnformatted("Sync settings");
|
||||||
ImGui::Text("Depth/color delay (us): %d", m_depthDelayOffColorUsec);
|
ImGui::Text("Depth/color delay (us): %d", m_depthDelayOffColorUsec);
|
||||||
ImGui::Text("Sync mode: %s", m_wiredSyncModeLabel.c_str());
|
ImGui::Text("Sync mode: %s", m_wiredSyncModeLabel.c_str());
|
||||||
ImGui::Text("Subordinate delay (us): %d", m_subordinateDelayOffMasterUsec);
|
ImGui::Text("Subordinate delay (us): %d", m_subordinateDelayOffMasterUsec);
|
||||||
|
@ -155,12 +157,18 @@ K4ADockControlStatus K4ARecordingDockControl::Show()
|
||||||
ImGui::Text("Recording Length (us): %lu", m_recordingLengthUsec);
|
ImGui::Text("Recording Length (us): %lu", m_recordingLengthUsec);
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
|
|
||||||
ImGui::Text("%s", "Device info");
|
ImGui::TextUnformatted("Device info");
|
||||||
ImGui::Text("Device S/N: %s", m_deviceSerialNumber.c_str());
|
ImGui::Text("Device S/N: %s", m_deviceSerialNumber.c_str());
|
||||||
ImGui::Text("RGB camera FW: %s", m_colorFirmwareVersion.c_str());
|
ImGui::Text("RGB camera FW: %s", m_colorFirmwareVersion.c_str());
|
||||||
ImGui::Text("Depth camera FW: %s", m_depthFirmwareVersion.c_str());
|
ImGui::Text("Depth camera FW: %s", m_depthFirmwareVersion.c_str());
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
|
|
||||||
|
if (!m_playbackThread->IsRunning())
|
||||||
|
{
|
||||||
|
ImGui::Text("Playback failed!");
|
||||||
|
return K4ADockControlStatus::Ok;
|
||||||
|
}
|
||||||
|
|
||||||
if (ImGui::Button("<|"))
|
if (ImGui::Button("<|"))
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(m_playbackThreadState.Mutex);
|
std::lock_guard<std::mutex> lock(m_playbackThreadState.Mutex);
|
||||||
|
@ -232,8 +240,13 @@ bool K4ARecordingDockControl::PlaybackThreadFn(PlaybackThreadState *state)
|
||||||
|
|
||||||
std::unique_lock<std::mutex> lock(state->Mutex);
|
std::unique_lock<std::mutex> lock(state->Mutex);
|
||||||
|
|
||||||
|
bool forceRefreshImuData = false;
|
||||||
if (state->SeekTimestamp != InvalidSeekTime)
|
if (state->SeekTimestamp != InvalidSeekTime)
|
||||||
{
|
{
|
||||||
|
// We need to read back a few seconds from before the time we seeked to.
|
||||||
|
//
|
||||||
|
forceRefreshImuData = true;
|
||||||
|
|
||||||
state->Recording.seek_timestamp(state->SeekTimestamp, K4A_PLAYBACK_SEEK_BEGIN);
|
state->Recording.seek_timestamp(state->SeekTimestamp, K4A_PLAYBACK_SEEK_BEGIN);
|
||||||
state->SeekTimestamp = InvalidSeekTime;
|
state->SeekTimestamp = InvalidSeekTime;
|
||||||
|
|
||||||
|
@ -248,6 +261,11 @@ bool K4ARecordingDockControl::PlaybackThreadFn(PlaybackThreadState *state)
|
||||||
backward = state->Step == StepDirection::Backward;
|
backward = state->Step == StepDirection::Backward;
|
||||||
state->Step = StepDirection::None;
|
state->Step = StepDirection::None;
|
||||||
|
|
||||||
|
// Stepping backwards is closer to a seek - we can't just add
|
||||||
|
// new samples on the end, we need to regenerate the graph
|
||||||
|
//
|
||||||
|
forceRefreshImuData |= backward;
|
||||||
|
|
||||||
// We don't want to restart from the beginning after stepping
|
// We don't want to restart from the beginning after stepping
|
||||||
// under most circumstances. If the user stepped to the last
|
// under most circumstances. If the user stepped to the last
|
||||||
// capture, we'll pick up on that when we try to read it and
|
// capture, we'll pick up on that when we try to read it and
|
||||||
|
@ -266,6 +284,7 @@ bool K4ARecordingDockControl::PlaybackThreadFn(PlaybackThreadState *state)
|
||||||
//
|
//
|
||||||
state->Recording.seek_timestamp(std::chrono::microseconds(0), K4A_PLAYBACK_SEEK_BEGIN);
|
state->Recording.seek_timestamp(std::chrono::microseconds(0), K4A_PLAYBACK_SEEK_BEGIN);
|
||||||
state->RecordingAtEnd = false;
|
state->RecordingAtEnd = false;
|
||||||
|
forceRefreshImuData = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
k4a::capture nextCapture;
|
k4a::capture nextCapture;
|
||||||
|
@ -293,10 +312,77 @@ bool K4ARecordingDockControl::PlaybackThreadFn(PlaybackThreadState *state)
|
||||||
|
|
||||||
state->CurrentCaptureTimestamp = GetCaptureTimestamp(nextCapture);
|
state->CurrentCaptureTimestamp = GetCaptureTimestamp(nextCapture);
|
||||||
|
|
||||||
// Update the images' timestamps using the timing data embedded in the recording
|
// Read IMU data up to the next timestamp, if applicable
|
||||||
// so we show comparable timestamps whenplaying back synchronized recordings
|
|
||||||
//
|
//
|
||||||
|
if (state->ImuPlaybackEnabled)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// On seek operations, we need to load historic data or the graph will be wrong.
|
||||||
|
// Move the IMU read pointer back enough samples to populate the entire graph (if available).
|
||||||
|
//
|
||||||
|
if (forceRefreshImuData)
|
||||||
|
{
|
||||||
|
state->ImuDataSource.ClearData();
|
||||||
|
|
||||||
|
k4a_imu_sample_t sample;
|
||||||
|
|
||||||
|
// Seek to the first IMU sample that was before the camera frame we're trying to show
|
||||||
|
//
|
||||||
|
while (state->Recording.get_previous_imu_sample(&sample))
|
||||||
|
{
|
||||||
|
if (sample.acc_timestamp_usec < static_cast<uint64_t>(state->CurrentCaptureTimestamp.count()))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then seek back the length of the graph
|
||||||
|
//
|
||||||
|
for (int i = 0; i < K4AImuGraphDataGenerator::SamplesPerGraph; ++i)
|
||||||
|
{
|
||||||
|
if (!state->Recording.get_previous_imu_sample(&sample))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read enough samples to catch up to the images that we're about to show
|
||||||
|
//
|
||||||
|
k4a_imu_sample_t nextImuSample;
|
||||||
|
nextImuSample.acc_timestamp_usec = 0;
|
||||||
|
|
||||||
|
while (nextImuSample.acc_timestamp_usec < static_cast<uint64_t>(state->CurrentCaptureTimestamp.count()))
|
||||||
|
{
|
||||||
|
if (!state->Recording.get_next_imu_sample(&nextImuSample))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the timestamps on the IMU samples using the timing data embedded in the recording
|
||||||
|
// so we show comparable timestamps when playing back synchronized recordings
|
||||||
|
//
|
||||||
|
nextImuSample.acc_timestamp_usec += static_cast<uint64_t>(state->TimestampOffset.count());
|
||||||
|
nextImuSample.gyro_timestamp_usec += static_cast<uint64_t>(state->TimestampOffset.count());
|
||||||
|
|
||||||
|
state->ImuDataSource.NotifyObservers(nextImuSample);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const k4a::error &e)
|
||||||
|
{
|
||||||
|
// If something went wrong while reading the IMU data, mark the IMU failed, but allow
|
||||||
|
// the camera playback to continue.
|
||||||
|
//
|
||||||
|
K4AViewerErrorManager::Instance().SetErrorStatus(e.what());
|
||||||
|
state->ImuDataSource.NotifyTermination();
|
||||||
|
state->ImuPlaybackEnabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the timestamps on the images using the timing data embedded in the recording
|
||||||
|
// so we show comparable timestamps when playing back synchronized recordings
|
||||||
|
//
|
||||||
k4a::image images[] = { nextCapture.get_color_image(),
|
k4a::image images[] = { nextCapture.get_color_image(),
|
||||||
nextCapture.get_depth_image(),
|
nextCapture.get_depth_image(),
|
||||||
nextCapture.get_ir_image() };
|
nextCapture.get_ir_image() };
|
||||||
|
@ -309,7 +395,7 @@ bool K4ARecordingDockControl::PlaybackThreadFn(PlaybackThreadState *state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
state->DataSource.NotifyObservers(nextCapture);
|
state->CaptureDataSource.NotifyObservers(nextCapture);
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
|
|
||||||
// Account for the time we spent getting captures and such when figuring out how long to wait
|
// Account for the time we spent getting captures and such when figuring out how long to wait
|
||||||
|
@ -369,12 +455,19 @@ void K4ARecordingDockControl::SetViewType(K4AWindowSet::ViewType viewType)
|
||||||
K4AWindowManager::Instance().ClearWindows();
|
K4AWindowManager::Instance().ClearWindows();
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(m_playbackThreadState.Mutex);
|
std::lock_guard<std::mutex> lock(m_playbackThreadState.Mutex);
|
||||||
|
|
||||||
|
K4ADataSource<k4a_imu_sample_t> *imuDataSource = nullptr;
|
||||||
switch (viewType)
|
switch (viewType)
|
||||||
{
|
{
|
||||||
case K4AWindowSet::ViewType::Normal:
|
case K4AWindowSet::ViewType::Normal:
|
||||||
|
if (m_recordConfiguration.imu_track_enabled)
|
||||||
|
{
|
||||||
|
m_playbackThreadState.ImuPlaybackEnabled = true;
|
||||||
|
imuDataSource = &m_playbackThreadState.ImuDataSource;
|
||||||
|
}
|
||||||
K4AWindowSet::StartNormalWindows(m_filenameLabel.c_str(),
|
K4AWindowSet::StartNormalWindows(m_filenameLabel.c_str(),
|
||||||
&m_playbackThreadState.DataSource,
|
&m_playbackThreadState.CaptureDataSource,
|
||||||
nullptr, // IMU playback not supported yet
|
imuDataSource,
|
||||||
nullptr, // Audio source - sound is not supported in recordings
|
nullptr, // Audio source - sound is not supported in recordings
|
||||||
m_recordingHasDepth,
|
m_recordingHasDepth,
|
||||||
m_recordConfiguration.depth_mode,
|
m_recordConfiguration.depth_mode,
|
||||||
|
@ -391,7 +484,7 @@ void K4ARecordingDockControl::SetViewType(K4AWindowSet::ViewType viewType)
|
||||||
m_recordConfiguration.color_format == K4A_IMAGE_FORMAT_COLOR_BGRA32;
|
m_recordConfiguration.color_format == K4A_IMAGE_FORMAT_COLOR_BGRA32;
|
||||||
K4AWindowSet::StartPointCloudWindow(m_filenameLabel.c_str(),
|
K4AWindowSet::StartPointCloudWindow(m_filenameLabel.c_str(),
|
||||||
std::move(calibration),
|
std::move(calibration),
|
||||||
&m_playbackThreadState.DataSource,
|
&m_playbackThreadState.CaptureDataSource,
|
||||||
colorPointCloudAvailable);
|
colorPointCloudAvailable);
|
||||||
}
|
}
|
||||||
catch (const k4a::error &e)
|
catch (const k4a::error &e)
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
#include "ik4adockcontrol.h"
|
#include "ik4adockcontrol.h"
|
||||||
#include "k4adatasource.h"
|
#include "k4adatasource.h"
|
||||||
|
#include "k4aimugraphdatagenerator.h"
|
||||||
#include "k4apollingthread.h"
|
#include "k4apollingthread.h"
|
||||||
#include "k4awindowset.h"
|
#include "k4awindowset.h"
|
||||||
|
|
||||||
|
@ -59,7 +60,10 @@ private:
|
||||||
// Recording state
|
// Recording state
|
||||||
//
|
//
|
||||||
k4a::playback Recording;
|
k4a::playback Recording;
|
||||||
K4ADataSource<k4a::capture> DataSource;
|
K4ADataSource<k4a::capture> CaptureDataSource;
|
||||||
|
K4ADataSource<k4a_imu_sample_t> ImuDataSource;
|
||||||
|
|
||||||
|
bool ImuPlaybackEnabled;
|
||||||
} m_playbackThreadState;
|
} m_playbackThreadState;
|
||||||
|
|
||||||
static bool PlaybackThreadFn(PlaybackThreadState *state);
|
static bool PlaybackThreadFn(PlaybackThreadState *state);
|
||||||
|
|
|
@ -43,6 +43,13 @@ public:
|
||||||
return m_count == 0;
|
return m_count == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Clear()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
m_count = 0;
|
||||||
|
m_buffer.fill(T());
|
||||||
|
}
|
||||||
|
|
||||||
bool Full()
|
bool Full()
|
||||||
{
|
{
|
||||||
return m_buffer.size() == m_count;
|
return m_buffer.size() == m_count;
|
||||||
|
|
|
@ -72,8 +72,6 @@ std::ostream &operator<<(std::ostream &s, const k4a_color_control_command_t &val
|
||||||
{
|
{
|
||||||
case K4A_COLOR_CONTROL_EXPOSURE_TIME_ABSOLUTE:
|
case K4A_COLOR_CONTROL_EXPOSURE_TIME_ABSOLUTE:
|
||||||
return s << "EXPOSURE_TIME_ABSOLUTE";
|
return s << "EXPOSURE_TIME_ABSOLUTE";
|
||||||
case K4A_COLOR_CONTROL_AUTO_EXPOSURE_PRIORITY:
|
|
||||||
return s << "AUTO_EXPOSURE_PRIORITY";
|
|
||||||
case K4A_COLOR_CONTROL_BRIGHTNESS:
|
case K4A_COLOR_CONTROL_BRIGHTNESS:
|
||||||
return s << "BRIGHTNESS";
|
return s << "BRIGHTNESS";
|
||||||
case K4A_COLOR_CONTROL_CONTRAST:
|
case K4A_COLOR_CONTROL_CONTRAST:
|
||||||
|
|
|
@ -85,7 +85,8 @@ public:
|
||||||
|
|
||||||
const bool imageIsHovered = ImGui::IsItemHovered();
|
const bool imageIsHovered = ImGui::IsItemHovered();
|
||||||
|
|
||||||
if (m_currentImage != nullptr && K4AViewerSettingsManager::Instance().GetShowInfoPane())
|
if (m_currentImage != nullptr &&
|
||||||
|
K4AViewerSettingsManager::Instance().GetViewerOption(ViewerOption::ShowInfoPane))
|
||||||
{
|
{
|
||||||
ImGui::SetNextWindowSizeConstraints(ImVec2(0.f, 0.f), displayDimensions);
|
ImGui::SetNextWindowSizeConstraints(ImVec2(0.f, 0.f), displayDimensions);
|
||||||
ImGui::SetNextWindowPos(imageStartPos, ImGuiCond_Always);
|
ImGui::SetNextWindowPos(imageStartPos, ImGuiCond_Always);
|
||||||
|
@ -149,7 +150,7 @@ private:
|
||||||
|
|
||||||
void RenderBasicInfoPane(const k4a::image &image)
|
void RenderBasicInfoPane(const k4a::image &image)
|
||||||
{
|
{
|
||||||
if (K4AViewerSettingsManager::Instance().GetShowFrameRateInfo())
|
if (K4AViewerSettingsManager::Instance().GetViewerOption(ViewerOption::ShowFrameRateInfo))
|
||||||
{
|
{
|
||||||
ImGui::Text("Average frame rate: %.2f fps", m_imageSource->GetFrameRate());
|
ImGui::Text("Average frame rate: %.2f fps", m_imageSource->GetFrameRate());
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
// Project headers
|
// Project headers
|
||||||
//
|
//
|
||||||
#include "k4aaudiomanager.h"
|
#include "k4aaudiomanager.h"
|
||||||
|
#include "k4alogdockcontrol.h"
|
||||||
#include "k4asourceselectiondockcontrol.h"
|
#include "k4asourceselectiondockcontrol.h"
|
||||||
#include "k4aviewererrormanager.h"
|
#include "k4aviewererrormanager.h"
|
||||||
#include "k4aviewerutil.h"
|
#include "k4aviewerutil.h"
|
||||||
|
@ -88,7 +89,7 @@ void APIENTRY glDebugOutput(GLenum source,
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
K4AViewer::K4AViewer(const K4AViewerOptions &args)
|
K4AViewer::K4AViewer(const K4AViewerArgs &args)
|
||||||
{
|
{
|
||||||
// Setup window
|
// Setup window
|
||||||
glfwSetErrorCallback(LogGlfwError);
|
glfwSetErrorCallback(LogGlfwError);
|
||||||
|
@ -145,6 +146,7 @@ K4AViewer::K4AViewer(const K4AViewerOptions &args)
|
||||||
}
|
}
|
||||||
|
|
||||||
K4AWindowManager::Instance().PushLeftDockControl(std14::make_unique<K4ASourceSelectionDockControl>());
|
K4AWindowManager::Instance().PushLeftDockControl(std14::make_unique<K4ASourceSelectionDockControl>());
|
||||||
|
K4AWindowManager::Instance().PushBottomDockControl(std14::make_unique<K4ALogDockControl>());
|
||||||
}
|
}
|
||||||
|
|
||||||
K4AViewer::~K4AViewer()
|
K4AViewer::~K4AViewer()
|
||||||
|
@ -222,35 +224,43 @@ void K4AViewer::ShowMainMenuBar()
|
||||||
{
|
{
|
||||||
if (ImGui::BeginMainMenuBar())
|
if (ImGui::BeginMainMenuBar())
|
||||||
{
|
{
|
||||||
if (ImGui::BeginMenu("File"))
|
if (ImGui::BeginMenu("Settings"))
|
||||||
{
|
{
|
||||||
auto &settings = K4AViewerSettingsManager::Instance();
|
ShowViewerOptionMenuItem("Show log dock", ViewerOption::ShowLogDock);
|
||||||
if (ImGui::MenuItem("Show info overlay", nullptr, settings.GetShowInfoPane()))
|
ShowViewerOptionMenuItem("Show info overlay", ViewerOption::ShowInfoPane);
|
||||||
settings.SetShowInfoPane(!settings.GetShowInfoPane());
|
|
||||||
if (settings.GetShowInfoPane())
|
if (K4AViewerSettingsManager::Instance().GetViewerOption(ViewerOption::ShowInfoPane))
|
||||||
{
|
{
|
||||||
if (ImGui::MenuItem("Show framerate info", nullptr, settings.GetShowFrameRateInfo()))
|
ShowViewerOptionMenuItem("Show framerate", ViewerOption::ShowFrameRateInfo);
|
||||||
settings.SetShowFrameRateInfo(!settings.GetShowFrameRateInfo());
|
|
||||||
}
|
}
|
||||||
if (ImGui::MenuItem("Show developer options", nullptr, m_showDeveloperOptions))
|
|
||||||
m_showDeveloperOptions = !m_showDeveloperOptions;
|
ShowViewerOptionMenuItem("Show developer options", ViewerOption::ShowDeveloperOptions);
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
if (ImGui::MenuItem("Load default settings"))
|
||||||
|
{
|
||||||
|
K4AViewerSettingsManager::Instance().SetDefaults();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
if (ImGui::MenuItem("Quit"))
|
if (ImGui::MenuItem("Quit"))
|
||||||
|
{
|
||||||
glfwSetWindowShouldClose(m_window, true);
|
glfwSetWindowShouldClose(m_window, true);
|
||||||
|
}
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_showDeveloperOptions)
|
if (K4AViewerSettingsManager::Instance().GetViewerOption(ViewerOption::ShowDeveloperOptions))
|
||||||
{
|
{
|
||||||
if (ImGui::BeginMenu("Developer"))
|
if (ImGui::BeginMenu("Developer"))
|
||||||
{
|
{
|
||||||
if (ImGui::MenuItem("Show demo window", nullptr, m_showDemoWindow))
|
ImGui::MenuItem("Show demo window", nullptr, &m_showDemoWindow);
|
||||||
m_showDemoWindow = !m_showDemoWindow;
|
ImGui::MenuItem("Show style editor", nullptr, &m_showStyleEditor);
|
||||||
if (ImGui::MenuItem("Show style editor", nullptr, m_showStyleEditor))
|
ImGui::MenuItem("Show metrics window", nullptr, &m_showMetricsWindow);
|
||||||
m_showStyleEditor = !m_showStyleEditor;
|
ImGui::MenuItem("Show perf counters", nullptr, &m_showPerfCounters);
|
||||||
if (ImGui::MenuItem("Show metrics window", nullptr, m_showMetricsWindow))
|
|
||||||
m_showMetricsWindow = !m_showMetricsWindow;
|
|
||||||
if (ImGui::MenuItem("Show perf counters", nullptr, m_showPerfCounters))
|
|
||||||
m_showPerfCounters = !m_showPerfCounters;
|
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -296,3 +306,14 @@ void K4AViewer::ShowErrorOverlay()
|
||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void K4AViewer::ShowViewerOptionMenuItem(const char *msg, ViewerOption option)
|
||||||
|
{
|
||||||
|
auto &settings = K4AViewerSettingsManager::Instance();
|
||||||
|
bool isSet = settings.GetViewerOption(option);
|
||||||
|
|
||||||
|
if (ImGui::MenuItem(msg, nullptr, isSet))
|
||||||
|
{
|
||||||
|
settings.SetViewerOption(option, !isSet);
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,8 +14,9 @@
|
||||||
|
|
||||||
// Project headers
|
// Project headers
|
||||||
//
|
//
|
||||||
|
#include "k4aviewersettingsmanager.h"
|
||||||
|
|
||||||
struct K4AViewerOptions
|
struct K4AViewerArgs
|
||||||
{
|
{
|
||||||
bool HighDpi = false;
|
bool HighDpi = false;
|
||||||
};
|
};
|
||||||
|
@ -25,7 +26,7 @@ namespace k4aviewer
|
||||||
class K4AViewer
|
class K4AViewer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit K4AViewer(const K4AViewerOptions &args);
|
explicit K4AViewer(const K4AViewerArgs &args);
|
||||||
~K4AViewer();
|
~K4AViewer();
|
||||||
|
|
||||||
void Run();
|
void Run();
|
||||||
|
@ -42,8 +43,9 @@ private:
|
||||||
|
|
||||||
void ShowErrorOverlay();
|
void ShowErrorOverlay();
|
||||||
|
|
||||||
|
void ShowViewerOptionMenuItem(const char *msg, ViewerOption option);
|
||||||
|
|
||||||
GLFWwindow *m_window;
|
GLFWwindow *m_window;
|
||||||
bool m_showDeveloperOptions = false;
|
|
||||||
bool m_showDemoWindow = false;
|
bool m_showDemoWindow = false;
|
||||||
bool m_showStyleEditor = false;
|
bool m_showStyleEditor = false;
|
||||||
bool m_showMetricsWindow = false;
|
bool m_showMetricsWindow = false;
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
// Project headers
|
// Project headers
|
||||||
//
|
//
|
||||||
#include "k4atypeoperators.h"
|
#include "k4atypeoperators.h"
|
||||||
|
#include "k4aviewersettingsmanager.h"
|
||||||
|
|
||||||
using namespace k4aviewer;
|
using namespace k4aviewer;
|
||||||
|
|
||||||
|
@ -37,6 +38,7 @@ void K4AViewerErrorManager::SetErrorStatus(const std::string &msg)
|
||||||
|
|
||||||
void K4AViewerErrorManager::SetErrorStatus(std::string &&msg)
|
void K4AViewerErrorManager::SetErrorStatus(std::string &&msg)
|
||||||
{
|
{
|
||||||
|
K4AViewerSettingsManager::Instance().SetViewerOption(ViewerOption::ShowLogDock, true);
|
||||||
std::lock_guard<std::mutex> lock(m_mutex);
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
m_errors.emplace(std::move(msg));
|
m_errors.emplace(std::move(msg));
|
||||||
}
|
}
|
||||||
|
@ -50,6 +52,7 @@ void K4AViewerErrorManager::SetErrorStatus(const char *msg, const k4a_buffer_res
|
||||||
|
|
||||||
void K4AViewerErrorManager::SetErrorStatus(const std::string &msg, k4a_buffer_result_t result)
|
void K4AViewerErrorManager::SetErrorStatus(const std::string &msg, k4a_buffer_result_t result)
|
||||||
{
|
{
|
||||||
|
K4AViewerSettingsManager::Instance().SetViewerOption(ViewerOption::ShowLogDock, true);
|
||||||
SetErrorStatus(msg.c_str(), result);
|
SetErrorStatus(msg.c_str(), result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,14 @@ GLenum K4AViewerImage::UpdateTexture(const uint8_t *data)
|
||||||
return glGetError();
|
return glGetError();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::copy(data, data + m_textureBufferSize, buffer);
|
if (data)
|
||||||
|
{
|
||||||
|
std::copy(data, data + m_textureBufferSize, buffer);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::fill(buffer, buffer + m_textureBufferSize, static_cast<uint8_t>(0));
|
||||||
|
}
|
||||||
|
|
||||||
if (!glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER))
|
if (!glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER))
|
||||||
{
|
{
|
||||||
|
@ -111,15 +118,7 @@ GLenum K4AViewerImage::Create(std::shared_ptr<K4AViewerImage> *out,
|
||||||
static_cast<GLsizei>(dimensions.Width),
|
static_cast<GLsizei>(dimensions.Width),
|
||||||
static_cast<GLsizei>(dimensions.Height));
|
static_cast<GLsizei>(dimensions.Height));
|
||||||
|
|
||||||
GLenum status;
|
GLenum status = newTexture->UpdateTexture(data);
|
||||||
if (data)
|
|
||||||
{
|
|
||||||
status = newTexture->UpdateTexture(data);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
status = glGetError();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status == GL_NO_ERROR)
|
if (status == GL_NO_ERROR)
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
// Associated header
|
||||||
|
//
|
||||||
|
#include "k4aviewerlogmanager.h"
|
||||||
|
|
||||||
|
// System headers
|
||||||
|
//
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// Library headers
|
||||||
|
//
|
||||||
|
|
||||||
|
// Project headers
|
||||||
|
//
|
||||||
|
|
||||||
|
using namespace k4aviewer;
|
||||||
|
|
||||||
|
K4AViewerLogManager &K4AViewerLogManager::Instance()
|
||||||
|
{
|
||||||
|
static K4AViewerLogManager instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
K4AViewerLogManager::K4AViewerLogManager()
|
||||||
|
{
|
||||||
|
if (k4a_set_debug_message_handler(&LoggerCallback, this, K4A_LOG_LEVEL_TRACE) != K4A_RESULT_SUCCEEDED)
|
||||||
|
{
|
||||||
|
Log(K4A_LOG_LEVEL_ERROR, __FILE__, __LINE__, "Failed to initialize K4A logging!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
K4AViewerLogManager::~K4AViewerLogManager()
|
||||||
|
{
|
||||||
|
(void)k4a_set_debug_message_handler(nullptr, nullptr, K4A_LOG_LEVEL_TRACE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void K4AViewerLogManager::Log(k4a_log_level_t severity, const char *file, int line, const char *msg)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
for (auto wpListener = m_listeners.begin(); wpListener != m_listeners.end();)
|
||||||
|
{
|
||||||
|
std::shared_ptr<IK4AViewerLogListener> spListener = wpListener->lock();
|
||||||
|
if (spListener)
|
||||||
|
{
|
||||||
|
spListener->Log(severity, file, line, msg);
|
||||||
|
++wpListener;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto toDelete = wpListener;
|
||||||
|
++wpListener;
|
||||||
|
m_listeners.erase(toDelete);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void K4AViewerLogManager::RegisterListener(std::shared_ptr<IK4AViewerLogListener> listener)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
m_listeners.emplace_back(std::move(listener));
|
||||||
|
}
|
||||||
|
|
||||||
|
void K4AViewerLogManager::LoggerCallback(void *context,
|
||||||
|
k4a_log_level_t level,
|
||||||
|
const char *file,
|
||||||
|
int line,
|
||||||
|
const char *msg)
|
||||||
|
{
|
||||||
|
K4AViewerLogManager *instance = reinterpret_cast<K4AViewerLogManager *>(context);
|
||||||
|
instance->Log(level, file, line, msg);
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
#ifndef K4AVIEWERLOGMANAGER_H
|
||||||
|
#define K4AVIEWERLOGMANAGER_H
|
||||||
|
|
||||||
|
// System headers
|
||||||
|
//
|
||||||
|
#include <list>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
// Library headers
|
||||||
|
//
|
||||||
|
#include <k4a/k4a.hpp>
|
||||||
|
|
||||||
|
// Project headers
|
||||||
|
//
|
||||||
|
|
||||||
|
namespace k4aviewer
|
||||||
|
{
|
||||||
|
class IK4AViewerLogListener
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void Log(k4a_log_level_t severity, const char *file, int line, const char *msg) = 0;
|
||||||
|
virtual ~IK4AViewerLogListener() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
class K4AViewerLogManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static K4AViewerLogManager &Instance();
|
||||||
|
|
||||||
|
void Log(k4a_log_level_t severity, const char *file, int line, const char *msg);
|
||||||
|
|
||||||
|
void RegisterListener(std::shared_ptr<IK4AViewerLogListener> listener);
|
||||||
|
|
||||||
|
private:
|
||||||
|
K4AViewerLogManager();
|
||||||
|
~K4AViewerLogManager();
|
||||||
|
|
||||||
|
static void LoggerCallback(void *context, k4a_log_level_t level, const char *file, int line, const char *msg);
|
||||||
|
|
||||||
|
std::mutex m_mutex;
|
||||||
|
std::list<std::weak_ptr<IK4AViewerLogListener>> m_listeners;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace k4aviewer
|
||||||
|
|
||||||
|
#endif
|
|
@ -16,18 +16,14 @@
|
||||||
|
|
||||||
// Project headers
|
// Project headers
|
||||||
//
|
//
|
||||||
|
#include "filesystem17.h"
|
||||||
#include "k4atypeoperators.h"
|
#include "k4atypeoperators.h"
|
||||||
|
|
||||||
using namespace k4aviewer;
|
using namespace k4aviewer;
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
constexpr char SettingsFileName[] = "k4aviewersettings.txt";
|
|
||||||
constexpr char Separator[] = " ";
|
constexpr char Separator[] = " ";
|
||||||
|
|
||||||
constexpr char ShowFramerateTag[] = "ShowFramerate";
|
|
||||||
constexpr char ShowInfoPaneTag[] = "ShowInfoPane";
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace k4aviewer
|
namespace k4aviewer
|
||||||
|
@ -153,6 +149,128 @@ std::istream &operator>>(std::istream &s, K4ADeviceConfiguration &val)
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr char BeginViewerOptionsTag[] = "BeginViewerOptions";
|
||||||
|
constexpr char EndViewerOptionsTag[] = "EndViewerOptions";
|
||||||
|
|
||||||
|
std::istream &operator>>(std::istream &s, K4AViewerOptions &val)
|
||||||
|
{
|
||||||
|
std::string variableTag;
|
||||||
|
s >> variableTag;
|
||||||
|
if (variableTag != BeginViewerOptionsTag)
|
||||||
|
{
|
||||||
|
s.setstate(std::ios::failbit);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (variableTag != EndDeviceConfigurationTag && s)
|
||||||
|
{
|
||||||
|
s >> variableTag;
|
||||||
|
if (variableTag == EndViewerOptionsTag)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ViewerOption option;
|
||||||
|
std::stringstream converter;
|
||||||
|
converter << variableTag;
|
||||||
|
converter >> option;
|
||||||
|
if (!converter)
|
||||||
|
{
|
||||||
|
s.setstate(std::ios::failbit);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool value;
|
||||||
|
s >> value;
|
||||||
|
if (!s)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
val.Options[static_cast<size_t>(option)] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream &operator<<(std::ostream &s, const K4AViewerOptions &val)
|
||||||
|
{
|
||||||
|
s << BeginViewerOptionsTag << std::endl;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < val.Options.size(); ++i)
|
||||||
|
{
|
||||||
|
s << static_cast<ViewerOption>(i) << Separator << val.Options[i] << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
s << EndViewerOptionsTag << std::endl;
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr char ShowFrameRateInfoTag[] = "ShowFrameRateInfo";
|
||||||
|
constexpr char ShowInfoPaneTag[] = "ShowInfoPane";
|
||||||
|
constexpr char ShowLogDockTag[] = "ShowLogDock";
|
||||||
|
constexpr char ShowDeveloperOptionsTag[] = "ShowDeveloperOptions";
|
||||||
|
|
||||||
|
std::istream &operator>>(std::istream &s, ViewerOption &val)
|
||||||
|
{
|
||||||
|
static_assert(static_cast<size_t>(ViewerOption::MAX) == 4, "Need to add a new viewer option conversion");
|
||||||
|
std::string tag;
|
||||||
|
s >> tag;
|
||||||
|
|
||||||
|
if (tag == ShowFrameRateInfoTag)
|
||||||
|
{
|
||||||
|
val = ViewerOption::ShowFrameRateInfo;
|
||||||
|
}
|
||||||
|
else if (tag == ShowInfoPaneTag)
|
||||||
|
{
|
||||||
|
val = ViewerOption::ShowInfoPane;
|
||||||
|
}
|
||||||
|
else if (tag == ShowLogDockTag)
|
||||||
|
{
|
||||||
|
val = ViewerOption::ShowLogDock;
|
||||||
|
}
|
||||||
|
else if (tag == ShowDeveloperOptionsTag)
|
||||||
|
{
|
||||||
|
val = ViewerOption::ShowDeveloperOptions;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
s.setstate(std::ios::failbit);
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream &operator<<(std::ostream &s, const ViewerOption &val)
|
||||||
|
{
|
||||||
|
static_assert(static_cast<size_t>(ViewerOption::MAX) == 4, "Need to add a new viewer option conversion");
|
||||||
|
|
||||||
|
switch (val)
|
||||||
|
{
|
||||||
|
case ViewerOption::ShowFrameRateInfo:
|
||||||
|
s << ShowFrameRateInfoTag;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ViewerOption::ShowInfoPane:
|
||||||
|
s << ShowInfoPaneTag;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ViewerOption::ShowLogDock:
|
||||||
|
s << ShowLogDockTag;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ViewerOption::ShowDeveloperOptions:
|
||||||
|
s << ShowDeveloperOptionsTag;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
s.setstate(std::ios::failbit);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
} // namespace k4aviewer
|
} // namespace k4aviewer
|
||||||
|
|
||||||
// The UI doesn't quite line up with the struct we actually need to give to the K4A API, so
|
// The UI doesn't quite line up with the struct we actually need to give to the K4A API, so
|
||||||
|
@ -177,70 +295,95 @@ k4a_device_configuration_t K4ADeviceConfiguration::ToK4ADeviceConfiguration() co
|
||||||
return deviceConfig;
|
return deviceConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
K4AViewerOptions::K4AViewerOptions()
|
||||||
|
{
|
||||||
|
static_assert(static_cast<size_t>(ViewerOption::MAX) == 4, "Need to add a new viewer option default");
|
||||||
|
|
||||||
|
Options[static_cast<size_t>(ViewerOption::ShowFrameRateInfo)] = false;
|
||||||
|
Options[static_cast<size_t>(ViewerOption::ShowInfoPane)] = true;
|
||||||
|
Options[static_cast<size_t>(ViewerOption::ShowLogDock)] = false;
|
||||||
|
Options[static_cast<size_t>(ViewerOption::ShowDeveloperOptions)] = false;
|
||||||
|
}
|
||||||
|
|
||||||
K4AViewerSettingsManager::K4AViewerSettingsManager()
|
K4AViewerSettingsManager::K4AViewerSettingsManager()
|
||||||
{
|
{
|
||||||
|
std17::filesystem::path settingsPath = "";
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
constexpr size_t bufferSize = 250;
|
||||||
|
char buffer[bufferSize];
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
// While not technically part of the C++ spec, MSVC provides the
|
||||||
|
// C function getenv_s in the C++ standard library headers and
|
||||||
|
// complains if you use getenv, so we use getenv_s here.
|
||||||
|
//
|
||||||
|
errno_t err = getenv_s(&len, buffer, bufferSize, "LOCALAPPDATA");
|
||||||
|
if (err == 0)
|
||||||
|
{
|
||||||
|
settingsPath = buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
// The Linux compilers, on the other hand, don't provide the 'safe'
|
||||||
|
// C functions, but they do provide a nonstandard function to do it,
|
||||||
|
// so we do that instead.
|
||||||
|
//
|
||||||
|
const char *home = secure_getenv("HOME");
|
||||||
|
if (home)
|
||||||
|
{
|
||||||
|
settingsPath = home;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
settingsPath.append(".k4aviewer");
|
||||||
|
m_settingsFilePath = settingsPath.string();
|
||||||
|
|
||||||
LoadSettings();
|
LoadSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
void K4AViewerSettingsManager::SaveSettings()
|
void K4AViewerSettingsManager::SaveSettings()
|
||||||
{
|
{
|
||||||
std::ofstream fileWriter(SettingsFileName);
|
std::ofstream fileWriter(m_settingsFilePath.c_str());
|
||||||
|
|
||||||
fileWriter << m_settingsPayload.SavedDeviceConfiguration << std::endl;
|
fileWriter << m_settingsPayload.SavedDeviceConfiguration << std::endl;
|
||||||
fileWriter << ShowFramerateTag << Separator << m_settingsPayload.ShowFrameRateInfo << std::endl;
|
fileWriter << m_settingsPayload.Options << std::endl;
|
||||||
fileWriter << ShowInfoPaneTag << Separator << m_settingsPayload.ShowInfoPane << std::endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void K4AViewerSettingsManager::LoadSettings()
|
void K4AViewerSettingsManager::LoadSettings()
|
||||||
{
|
{
|
||||||
std::ifstream fileReader(SettingsFileName);
|
std::ifstream fileReader(m_settingsFilePath.c_str());
|
||||||
if (!fileReader)
|
if (!fileReader)
|
||||||
{
|
{
|
||||||
|
SetDefaults();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool readSettingsFailed = false;
|
|
||||||
SettingsPayload newSettingsPayload;
|
SettingsPayload newSettingsPayload;
|
||||||
|
|
||||||
fileReader >> newSettingsPayload.SavedDeviceConfiguration;
|
fileReader >> newSettingsPayload.SavedDeviceConfiguration;
|
||||||
bool readFailed = fileReader.fail();
|
fileReader >> newSettingsPayload.Options;
|
||||||
while (!readFailed && !fileReader.eof())
|
|
||||||
{
|
|
||||||
std::string variableTag;
|
|
||||||
fileReader >> variableTag;
|
|
||||||
|
|
||||||
if (variableTag == ShowFramerateTag)
|
if (fileReader.fail())
|
||||||
{
|
|
||||||
fileReader >> newSettingsPayload.ShowFrameRateInfo;
|
|
||||||
}
|
|
||||||
else if (variableTag == ShowInfoPaneTag)
|
|
||||||
{
|
|
||||||
fileReader >> newSettingsPayload.ShowInfoPane;
|
|
||||||
}
|
|
||||||
else if (variableTag.empty())
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Mark the file as corrupt
|
|
||||||
//
|
|
||||||
readFailed = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (readFailed)
|
|
||||||
{
|
{
|
||||||
// File is corrupt, try to delete it
|
// File is corrupt, try to delete it
|
||||||
//
|
//
|
||||||
readSettingsFailed = true;
|
|
||||||
fileReader.close();
|
fileReader.close();
|
||||||
(void)std::remove(SettingsFileName);
|
SetDefaults();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
if (!readSettingsFailed)
|
|
||||||
{
|
{
|
||||||
m_settingsPayload = newSettingsPayload;
|
m_settingsPayload = newSettingsPayload;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void K4AViewerSettingsManager::SetDefaults()
|
||||||
|
{
|
||||||
|
if (!m_settingsFilePath.empty())
|
||||||
|
{
|
||||||
|
(void)std::remove(m_settingsFilePath.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
m_settingsPayload = SettingsPayload();
|
||||||
|
}
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче