Merge pull request #286 from Microsoft/develop

Merge develop into feature/csharp
This commit is contained in:
Brent Allen 2019-04-26 15:27:05 -07:00 коммит произвёл GitHub
Родитель be1bb68000 3e862f87a2
Коммит 7cfa67724a
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
106 изменённых файлов: 3586 добавлений и 1386 удалений

3
.gitmodules поставляемый
Просмотреть файл

@ -32,3 +32,6 @@
[submodule "extern/libjpeg-turbo/src"]
path = extern/libjpeg-turbo/src
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)
find_package(Doxygen 1.8.14 EXACT)
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})
# These variables are used in Doxyfile.in

6
CODE_OF_CONDUCT.md Normal file
Просмотреть файл

@ -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).
# 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
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'
value: true
- name: 'linux_firmware_version'
value: '1.5.946614'
value: '1.6.987014'
- name: 'windows_firmware_version'
value: '1.5.926614'
value: '1.6.987014'
trigger:
batch: false
@ -490,7 +490,7 @@ jobs:
command: download
vstsFeed: 'DepthEnginePlugin'
vstsFeedPackage: 'depthengine'
vstsPackageVersion: "1.0.1"
vstsPackageVersion: "1.0.5"
downloadDirectory: "$(System.ArtifactsDirectory)/depthengineplugin"
- task: CopyFiles@2
@ -516,7 +516,7 @@ jobs:
filePath: './scripts/Reset-Device.ps1'
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)'
displayName: 'Update Device'
@ -582,7 +582,7 @@ jobs:
command: download
vstsFeed: 'DepthEnginePlugin'
vstsFeedPackage: 'depthengine'
vstsPackageVersion: "1.0.1"
vstsPackageVersion: "1.0.5"
downloadDirectory: "$(System.ArtifactsDirectory)/depthengineplugin"
- task: UniversalPackages@0
@ -634,7 +634,7 @@ jobs:
workingDirectory: '$(System.ArtifactsDirectory)/findconnectedport/linux/'
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)'
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-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-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"))
# Added in clang 5
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})
else()
message(FATAL_ERROR "Unknown C++ compiler: ${CMAKE_CXX_COMPILER_ID}")
endif()
endif()

Двоичные данные
docs/Architecture.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 65 KiB

Двоичные данные
docs/SDK Internal Architecture.vsdx

Двоичный файл не отображается.

Просмотреть файл

@ -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
when a new firmware candidate is given and will require hardware. Firmware
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
directory.
can be run using "bin\firmware_fw.exe -ff \<factory firmware\> -lf \<lkg firmware\>
-tf \<test firmware\> -cf \<candidate firmware\>" in the build directory.
## Running tests

Просмотреть файл

@ -3,6 +3,7 @@
add_subdirectory(calibration)
add_subdirectory(enumerate)
add_subdirectory(playback_external_sync)
add_subdirectory(fastpointcloud)
add_subdirectory(k4arecorder)
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 ignored "-Wdocumentation"
#pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
#pragma clang diagnostic ignored "-Wdouble-promotion"
#endif
#include <GL/gl3w.h>

53
extern/libusb/CMakeLists.txt поставляемый
Просмотреть файл

@ -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")
# Setup install
include(GNUInstallDirs)
install(
FILES
$<TARGET_FILE:LibUSB::LibUSB>
DESTINATION
${CMAKE_INSTALL_BINDIR}
)
endif()
target_sources(LibUSB PRIVATE
"${CMAKE_CURRENT_SOURCE_DIR}/src/libusb/os/poll_windows.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/libusb/os/threads_windows.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/libusb/os/windows_nt_common.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/libusb/os/windows_usbdk.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/libusb/os/windows_winusb.c")
target_include_directories(LibUSB PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/msvc)
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()

162
extern/libusb/config.h поставляемый Normal file
Просмотреть файл

@ -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

1
extern/libusb/src поставляемый Submodule

@ -0,0 +1 @@
Subproject commit 0034b2afdcdb1614e78edaa2a9e22d5936aeae5d

2
extern/libuvc/CMakeLists.txt поставляемый
Просмотреть файл

@ -1,7 +1,5 @@
if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
if (NOT TARGET project_libuvc)
find_package(LibUSB REQUIRED)
set(CMAKE_BUILD_TARGET Static)
include(ExternalProject)

Просмотреть файл

@ -37,6 +37,54 @@ extern "C" {
*/
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.
*
* \param index

Просмотреть файл

@ -181,7 +181,8 @@ K4A_DECLARE_HANDLE(k4a_image_t);
K4A_DECLARE_HANDLE(k4a_transformation_t);
/*
* environment variables
* Environment Variables
*
* K4A_ENABLE_LOG_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
@ -199,6 +200,8 @@ K4A_DECLARE_HANDLE(k4a_transformation_t);
* 'i' - log all messages of level 'info' or higher criticality
* 't' - log all messages of level 'trace' 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.
@ -223,28 +226,6 @@ K4A_DECLARE_HANDLE(k4a_transformation_t);
*/
#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.
*
* \xmlonly
@ -309,6 +290,24 @@ typedef enum
*/
#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.
*
* \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_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
}
#endif

Просмотреть файл

@ -8,7 +8,6 @@
#define ALLOCATOR_H
#include <k4a/k4atypes.h>
#include <k4ainternal/logging.h>
#include <stddef.h>

Просмотреть файл

@ -9,7 +9,6 @@
#include <k4a/k4atypes.h>
#include <k4ainternal/handle.h>
#include <k4ainternal/logging.h>
#include <k4ainternal/usbcommand.h>
#include <k4ainternal/allocator.h>

Просмотреть файл

@ -9,7 +9,6 @@
#include <k4a/k4atypes.h>
#include <k4ainternal/handle.h>
#include <k4ainternal/logging.h>
#include <k4ainternal/depth_mcu.h>
#include <k4ainternal/calibration.h>

Просмотреть файл

@ -10,7 +10,6 @@
#include <k4a/k4atypes.h>
#include <k4ainternal/common.h>
#include <k4ainternal/handle.h>
#include <k4ainternal/logging.h>
#include <k4ainternal/usbcommand.h>
#include <k4ainternal/allocator.h>

Просмотреть файл

@ -9,7 +9,6 @@
#include <k4a/k4atypes.h>
#include <k4ainternal/handle.h>
#include <k4ainternal/logging.h>
#include <k4ainternal/allocator.h>
#ifdef __cplusplus

Просмотреть файл

@ -9,7 +9,6 @@
#include <k4a/k4atypes.h>
#include <k4ainternal/handle.h>
#include <k4ainternal/logging.h>
#include <k4ainternal/color_mcu.h>
#include <k4ainternal/calibration.h>
#include <azure_c_shared_utility/tickcounter.h>

Просмотреть файл

@ -33,17 +33,13 @@ extern "C" {
*/
K4A_DECLARE_HANDLE(logger_t);
/** Logger zones
*/
#define LOGGER_K4A "K4A"
/** Default logging settings
*/
#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_LOG_LEVEL "K4A_LOG_LEVEL"
#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
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_level = K4A_LOG_LEVEL;
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.
@ -95,35 +91,32 @@ void logger_destroy(logger_t logger_handle);
*/
bool logger_is_file_based(void);
/** Logs a message to the configured logger at an 'critical' level
* */
void logger_critical(const char *zone, const char *format, ...);
/** Registers a callback function to deliver messages to.
*
* \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_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, ...);
void logger_log(k4a_log_level_t level, const char *file, const int line, const char *format, ...);
FORCEINLINE k4a_result_t
TraceError(k4a_result_t result, const char *szCall, const char *szFile, int line, const char *szFunction)
{
if (K4A_FAILED(result))
{
// Example print:
// 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);
logger_log(K4A_LOG_LEVEL_ERROR, szFile, line, "%s returned failure in %s()", szCall, szFunction);
}
return result;
}
@ -132,10 +125,7 @@ TraceBufferError(k4a_buffer_result_t result, const char *szCall, const char *szF
{
if (result == K4A_BUFFER_RESULT_FAILED)
{
// Example print:
// 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);
logger_log(K4A_LOG_LEVEL_ERROR, szFile, line, "%s returned failure in %s()", szCall, szFunction);
}
return result;
}
@ -144,10 +134,7 @@ TraceWaitError(k4a_wait_result_t result, const char *szCall, const char *szFile,
{
if (result == K4A_WAIT_RESULT_FAILED)
{
// Example print:
// 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);
logger_log(K4A_LOG_LEVEL_ERROR, szFile, line, "%s returned failure in %s()", szCall, szFunction);
}
return result;
}
@ -156,14 +143,11 @@ FORCEINLINE k4a_result_t TraceReturn(k4a_result_t result, const char *szFile, in
{
if (K4A_FAILED(result))
{
// Example print:
// depth.cpp (86): depth_create returned failure.
logger_error(LOGGER_K4A, "%s (%d): %s() returned failure.", szFile, line, szFunction);
logger_log(K4A_LOG_LEVEL_ERROR, szFile, line, "%s() returned failure.", szFunction);
}
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;
}
@ -172,9 +156,7 @@ FORCEINLINE void TraceArg(int result, const char *szFile, int line, const char *
{
if (!result)
{
// Example print:
// 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);
logger_log(K4A_LOG_LEVEL_ERROR, szFile, line, "Invalid argument to %s(). %s", szFunction, szExpression);
}
}
@ -188,17 +170,14 @@ FORCEINLINE void TraceInvalidHandle(int result,
{
if (!result)
{
// Example print:
// depth.cpp (86): Invalid argument to depth_create. depth_handle (00000000) is not a valid handle of type
// depth_t
logger_error(LOGGER_K4A,
"%s (%d): Invalid argument to %s(). %s (%p) is not a valid handle of type %s",
szFile,
line,
szFunction,
szExpression,
pHandleValue,
szHandleType);
logger_log(K4A_LOG_LEVEL_ERROR,
szFile,
line,
"Invalid argument to %s(). %s (%p) is not a valid handle of type %s",
szFunction,
szExpression,
pHandleValue,
szHandleType);
}
}
@ -278,15 +257,17 @@ FORCEINLINE void TraceInvalidHandle(int result,
// Logs a 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, ...) \
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, ...) \
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, ...) \
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, ...) \
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
}

Просмотреть файл

@ -15,6 +15,10 @@
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
{
// 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;
k4a_record_configuration_t record_config;
k4a_image_format_t color_format_conversion;
std::unique_ptr<libebml::EbmlStream> stream;
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);
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 *get_track_by_tag(k4a_playback_context_t *context, const char *tag_name);
libmatroska::KaxTrackEntry *find_track(k4a_playback_context_t *context, const char *name, const char *tag_name);
libmatroska::KaxTag *get_tag(k4a_playback_context_t *context, const char *name);
std::string get_tag_string(libmatroska::KaxTag *tag);
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 *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);
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_next_cluster(k4a_playback_context_t *context,
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);
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_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);

Просмотреть файл

@ -8,7 +8,6 @@
#define QUEUE_H
#include <k4a/k4atypes.h>
#include <k4ainternal/logging.h>
#include <k4ainternal/capture.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);
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(
const k4a_calibration_t *calibration,
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,
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(
const k4a_calibration_t *calibration,
const k4a_transformation_xy_tables_t *xy_tables_depth_camera,

Просмотреть файл

@ -10,7 +10,6 @@
//************************ Includes *****************************
#include <k4a/k4atypes.h>
#include <k4ainternal/image.h>
#include <k4ainternal/logging.h>
#include <k4ainternal/common.h>
#ifdef __cplusplus
@ -33,7 +32,7 @@ typedef enum
typedef enum
{
CMD_STATUS_PASS = 0,
} usb_cmd_responces_t;
} usb_cmd_responses_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,
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.
*
* \param playback_handle

Просмотреть файл

@ -32,5 +32,6 @@ sudo apt install -y \
libxrandr-dev \
libusb-1.0-0-dev \
libssl-dev \
libudev-dev \
mesa-common-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 ||
g_allocated_image_count_imu || g_allocated_image_count_usb_depth || g_allocated_image_count_usb_imu)
{
logger_critical(LOGGER_K4A,
"Leaked usr:%d, color:%d, depth:%d, imu:%d, usb depth:%d, usb imu%d",
g_allocated_image_count_user,
g_allocated_image_count_color,
g_allocated_image_count_depth,
g_allocated_image_count_imu,
g_allocated_image_count_usb_depth,
g_allocated_image_count_usb_imu);
logger_log(K4A_LOG_LEVEL_CRITICAL,
__FILE__,
__LINE__,
"Leaked usr:%d, color:%d, depth:%d, imu:%d, usb depth:%d, usb imu%d",
g_allocated_image_count_user,
g_allocated_image_count_color,
g_allocated_image_count_depth,
g_allocated_image_count_imu,
g_allocated_image_count_usb_depth,
g_allocated_image_count_usb_imu);
}
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);
}
// 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)
{
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;
}
#ifdef __clang__
#pragma clang diagnostic pop
#endif
float capture_get_temperature_c(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 MetadataId_FrameAlignInfo 0x80000001
#pragma pack(push)
#pragma pack(push, 1)
typedef struct tag_CUSTOM_METADATA_FrameAlignInfo
{
KSCAMERA_METADATA_ITEMHEADER Header;
@ -22,6 +22,7 @@ typedef struct tag_CUSTOM_METADATA_FrameAlignInfo
ULONG PTSReference;
ULONGLONG USBSoFSeqNum; // 8 bytes
ULONGLONG USBSoFPTS; // 8 bytes
ULONG Synced;
} CUSTOM_METADATA_FrameAlignInfo, *PKSCAMERA_CUSTOM_METADATA_FrameAlignInfo;
#pragma pack(pop)

Просмотреть файл

@ -223,53 +223,58 @@ void depth_destroy(depth_t depth_handle)
static void log_device_info(depth_context_t *depth)
{
typedef void(logger_pfn_t)(const char *zone, const char *format, ...);
logger_pfn_t *logger = NULL;
k4a_log_level_t level;
if (logger_is_file_based())
{
// 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
// revision (hardware revision)
logger = logger_critical;
level = K4A_LOG_LEVEL_CRITICAL;
}
else
{
// 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
// or higher
logger = logger_info;
level = K4A_LOG_LEVEL_INFO;
}
logger(LOGGER_K4A, "******************** Device Info ********************");
logger(LOGGER_K4A, "K4A SDK version: %s", K4A_VERSION_STR);
logger_log(level, __FILE__, __LINE__, "******************** Device Info ********************");
logger_log(level, __FILE__, __LINE__, "K4A SDK version: %s", K4A_VERSION_STR);
char serial_number[128];
size_t size = sizeof(serial_number);
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;
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;
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;
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;
logger(LOGGER_K4A, "Sensor Config: %d.%d", ver->major, ver->minor);
logger(LOGGER_K4A, "Build type: %s", depth->version.firmware_build == 0 ? "Release" : "Debug");
logger(LOGGER_K4A,
"Signature type: %s",
depth->version.firmware_signature == K4A_FIRMWARE_SIGNATURE_MSFT ?
"MSFT" :
(depth->version.firmware_signature == K4A_FIRMWARE_SIGNATURE_TEST ? "Test" : "Unsigned"));
logger_log(level, __FILE__, __LINE__, "Sensor Config: %d.%d", ver->major, ver->minor);
logger_log(level,
__FILE__,
__LINE__,
"Build type: %s",
depth->version.firmware_build == 0 ? "Release" : "Debug");
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

Просмотреть файл

@ -57,6 +57,31 @@ typedef enum
DEV_CMD_MAX_CMD // Keep last
} 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_SET */

Просмотреть файл

@ -32,6 +32,7 @@ typedef struct _dewrapper_context_t
THREAD_HANDLE thread;
LOCK_HANDLE lock;
COND_HANDLE condition;
volatile bool thread_started;
volatile bool thread_stop;
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
// startup status.
Lock(dewrapper->lock);
dewrapper->thread_started = true;
dewrapper->thread_start_result = result;
Condition_Post(dewrapper->condition);
Unlock(dewrapper->lock);
@ -519,6 +521,7 @@ k4a_result_t dewrapper_start(dewrapper_t dewrapper_handle,
dewrapper->fps = config->camera_fps;
dewrapper->depth_mode = config->depth_mode;
dewrapper->thread_stop = false;
dewrapper->thread_started = false;
THREADAPI_RESULT tresult = ThreadAPI_Create(&dewrapper->thread, depth_engine_thread, dewrapper);
result = K4A_RESULT_FROM_BOOL(tresult == THREADAPI_OK);
@ -527,9 +530,12 @@ k4a_result_t dewrapper_start(dewrapper_t dewrapper_handle,
{
Lock(dewrapper->lock);
locked = true;
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 (!dewrapper->thread_started)
{
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))

Просмотреть файл

@ -6,6 +6,7 @@
#include <azure_c_shared_utility/envvariable.h>
#include <azure_c_shared_utility/refcount.h>
#include <azure_c_shared_utility/threadapi.h>
// System dependencies
#include <stdlib.h>
@ -27,33 +28,112 @@
extern "C" {
#endif
// Logger used for capturing errors from this module
static std::shared_ptr<spdlog::logger> g_logger = NULL;
static volatile long g_logger_count = 0;
static bool g_logger_is_file_based = false;
//
// This logger implements two different loggers, that have different lifespans.
// 1) A streaming callback function can deliver debug messages to a registered callback function.
// 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;
} 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"
// 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_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)
{
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, config->max_log_size == 0);
logger_context_t *context = logger_t_create(logger_handle);
const char *enable_file_logging = NULL;
const char *enable_stdout_logging = NULL;
const char *logging_level = NULL;
const char *enable_file_logging = nullptr;
const char *enable_stdout_logging = nullptr;
const char *logging_level = nullptr;
// 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)
@ -75,15 +155,16 @@ k4a_result_t logger_create(logger_config_t *config, logger_t *logger_handle)
// the post opperation value
static_assert(0, "This configuration needs to be confirmed");
#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
context->logger = g_env_logger;
return K4A_RESULT_SUCCEEDED;
}
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')
{
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)
{
// Create a file rotating logger with 5mb size max and 3 rotated files
g_logger = spdlog::rotating_logger_mt(K4A_LOGGER, log_file, config->max_log_size, LOG_FILE_MAX_FILES);
// Create a file rotating logger with 50mb size max and 3 rotated files
g_env_logger = spdlog::rotating_logger_mt(K4A_LOGGER, log_file, config->max_log_size, LOG_FILE_MAX_FILES);
spdlog::set_pattern("%v");
g_logger->info("\n\nNew logging session started\n");
g_logger_is_file_based = true;
g_env_logger->info("\n\nNew logging session started\n");
g_env_logger_is_file_based = true;
}
}
// log to STD out if file logger not created and STDOUT logging is not disabled.
if (g_logger == NULL)
// log to stdout if enabled via ENV var AND if file logging is not enabled.
if (g_env_logger == NULL)
{
if (enable_stdout_logging == NULL || enable_stdout_logging[0] == '\0' ||
(enable_stdout_logging && enable_stdout_logging[0] != '0'))
bool enable_stdout_logger = false;
// 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
// https://gitlab.kitware.com/cmake/cmake/issues/17620
g_logger = spdlog::stdout_logger_mt(K4A_LOGGER);
enable_stdout_logger = true;
}
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>
// https://github.com/gabime/spdlog/wiki/3.-Custom-formatting
spdlog::set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%^%l%$] [t=%t] %v");
// Set the default logging level
spdlog::set_level(spdlog::level::err);
// Set the default logging level SPD will allow. g_env_log_level will furthar refine this.
spdlog::set_level(spdlog::level::trace);
// override the default logging level
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')
{
// 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')
{
// 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')
{
// 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')
{
// 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')
{
// 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;
}
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);
// destroy the logger
if (DEC_REF_VAR(g_logger_count) == 0)
// Destroy the logger
if (DEC_REF_VAR(g_env_logger_count) == 0)
{
bool drop_logger = g_logger != NULL;
g_logger = NULL;
bool drop_logger = g_env_logger != NULL;
g_env_logger = NULL;
if (drop_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);
}
@ -199,125 +299,76 @@ void logger_destroy(logger_t logger_handle)
// Enable printf type checking in clang and gcc
__attribute__((__format__ (__printf__, 2, 0)))
#endif
void logger_trace(
const char * zone,
const char * format,
...)
void logger_log(k4a_log_level_t level, const char * file, const int line, const char *format, ...)
{
char buffer[1024];
if (g_logger == NULL)
// Quick exit if we are not logging the message
if (level > g_env_log_level && level > g_user_log_level)
{
return;
}
va_list args;
va_start(args, format);
vsnprintf(buffer, sizeof(buffer), format, args);
g_logger->trace("[{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)))
if (g_env_logger || g_user_logger_cb_info)
{
char buffer[1024];
va_list args;
va_start(args, format);
#ifndef _WIN32
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
#endif
void logger_info(
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->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)))
vsnprintf(buffer, sizeof(buffer), format, args);
#ifndef _WIN32
#pragma GCC diagnostic pop
#endif
void logger_warn(
const char * zone,
const char * format,
...)
{
char buffer[1024];
va_end(args);
if (g_logger == NULL)
{
return;
if ((level <= g_user_log_level) && (g_user_log_level != K4A_LOG_LEVEL_OFF))
{
// 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()
{
return g_logger_is_file_based;
return g_env_logger_is_file_based;
}
#ifdef __cplusplus

Просмотреть файл

@ -31,6 +31,8 @@ target_link_libraries(k4a_playback PUBLIC
k4ainternal::logging
ebml::ebml
matroska::matroska
libyuv::libyuv
libjpeg-turbo::libjpeg-turbo
)
# Define alias for other targets to link against

Просмотреть файл

@ -12,6 +12,9 @@
#include <k4ainternal/common.h>
#include <k4ainternal/logging.h>
#include <turbojpeg.h>
#include <libyuv.h>
using namespace LIBMATROSKA_NAMESPACE;
namespace k4arecord
@ -24,6 +27,22 @@ std::unique_ptr<EbmlElement> next_child(k4a_playback_context_t *context, EbmlEle
EbmlElement *element =
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);
}
catch (std::ios_base::failure &e)
@ -220,17 +239,17 @@ k4a_result_t parse_mkv(k4a_playback_context_t *context)
KaxSimpleBlock *simple_block = NULL;
KaxBlockGroup *block_group = NULL;
std::shared_ptr<loaded_cluster_t> last_cluster = load_cluster(context, cluster_info);
if (last_cluster == nullptr || last_cluster->cluster == nullptr)
std::shared_ptr<KaxCluster> last_cluster = load_cluster_internal(context, cluster_info);
if (last_cluster == nullptr)
{
LOG_ERROR("Failed to load end of recording.", 0);
return K4A_RESULT_FAILED;
}
for (EbmlElement *e : last_cluster->cluster->GetElementList())
for (EbmlElement *e : last_cluster->GetElementList())
{
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();
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))
{
block_group->SetParent(*last_cluster->cluster);
block_group->SetParent(*last_cluster);
KaxTrackEntry *parent_track = NULL;
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->color_track.track = get_track_by_tag(context, "K4A_COLOR_MODE");
context->depth_track.track = get_track_by_tag(context, "K4A_DEPTH_MODE");
context->ir_track.track = get_track_by_tag(context, "K4A_IR_MODE");
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");
}
context->color_track.track = find_track(context, "COLOR", "K4A_COLOR_TRACK");
context->depth_track.track = find_track(context, "DEPTH", "K4A_DEPTH_TRACK");
context->ir_track.track = find_track(context, "IR", "K4A_IR_TRACK");
if (context->ir_track.track == NULL)
{
context->ir_track.track = get_track_by_name(context, "IR");
}
if (context->imu_track.track == NULL)
{
context->imu_track.track = get_track_by_name(context, "IMU");
// Support legacy IR track naming.
context->ir_track.track = find_track(context, "DEPTH_IR", NULL);
}
context->imu_track.track = find_track(context, "IMU", "K4A_IMU_TRACK");
// Read device calibration attachment
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_format = context->color_track.format;
context->color_format_conversion = context->color_track.format;
}
else
{
context->record_config.color_resolution = K4A_COLOR_RESOLUTION_OFF;
// Set to a default color format if color track is disabled.
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");
@ -452,26 +462,48 @@ k4a_result_t parse_recording_config(k4a_playback_context_t *context)
LOG_ERROR("K4A_DEPTH_MODE tag is missing.", 0);
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_height = 0;
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)
{
context->record_config.depth_mode = depth_modes[i].first;
break;
if (k4a_convert_depth_mode_to_width_height(depth_modes[i].first, &depth_width, &depth_height))
{
context->record_config.depth_mode = 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->record_config.depth_mode == K4A_DEPTH_MODE_OFF)
{
// Try to find the mode matching strings in the legacy modes
for (size_t i = 0; i < arraysize(legacy_depth_modes); i++)
{
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)
@ -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));
if (context->ir_track.format == K4A_IMAGE_FORMAT_DEPTH16)
{
context->ir_track.format = K4A_IMAGE_FORMAT_IR16;
}
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;
}
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->tracks == nullptr);
RETURN_VALUE_IF_ARG(NULL, name == NULL);
std::string search(name);
KaxTrackEntry *track = NULL;
for (EbmlElement *e : context->tracks->GetElementList())
std::string search_name(name);
uint64_t search_uid = 0;
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);
return track;
search_uid = GetChild<KaxTagTrackUID>(tagTargets).GetValue();
}
}
}
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 (search_uid == 0)
{
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);
return track;
LOG_ERROR("Track tag '%s' for track %s is not valid.", tag_name, name);
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;
}
@ -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.
// 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->ebml_file == nullptr);
@ -1518,6 +1561,206 @@ static void free_vector_buffer(void *buffer, void *context)
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)
{
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->reader == 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)
{
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_result_t result = K4A_RESULT_SUCCEEDED;
if (block->reader == &context->color_track)
{
result = TRACE_CALL(k4a_image_create_from_buffer(context->record_config.color_format,
(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);
result = TRACE_CALL(convert_block_to_image(context, block, &image_handle, context->color_format_conversion));
k4a_capture_set_color_image(*capture_handle, image_handle);
}
else if (block->reader == &context->depth_track)
{
result = TRACE_CALL(k4a_image_create_from_buffer(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);
result = TRACE_CALL(convert_block_to_image(context, block, &image_handle, K4A_IMAGE_FORMAT_DEPTH16));
k4a_capture_set_depth_image(*capture_handle, image_handle);
}
else if (block->reader == &context->ir_track)
{
result = TRACE_CALL(k4a_image_create_from_buffer(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);
result = TRACE_CALL(convert_block_to_image(context, block, &image_handle, K4A_IMAGE_FORMAT_IR16));
k4a_capture_set_ir_image(*capture_handle, image_handle);
}
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);
}
if (K4A_FAILED(result))
{
delete buffer;
}
return result;
}
@ -1634,7 +1826,7 @@ k4a_stream_result_t get_capture(k4a_playback_context_t *context, k4a_capture_t *
{
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)
{
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;
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,
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->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++)
{
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, cluster == NULL);
k4a_result_t result = K4A_RESULT_SUCCEEDED;
if (cluster->data.size() == 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
@ -307,6 +307,8 @@ k4a_result_t write_cluster(k4a_record_context_t *context, cluster_t *cluster, ui
KaxTrackEntry *current_track = NULL;
uint64_t block_blob_start = 0;
std::vector<std::unique_ptr<KaxBlockBlob>> blob_list;
bool first = true;
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
block_blob = new KaxBlockBlob(data.second.track == context->imu_track ? BLOCK_BLOB_NO_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_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);
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);
}
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;
}

Просмотреть файл

@ -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)
{
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);
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);
}
@ -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);
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);
}
}
@ -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);
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,
"K4A_IR_MODE",
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");
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);
return K4A_RESULT_SUCCEEDED;
@ -643,6 +655,11 @@ k4a_result_t k4a_record_flush(const k4a_record_t recording_handle)
{ // Update seek info
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.IndexThis(segment_info, *context->file_segment);

Просмотреть файл

@ -62,6 +62,13 @@ uint32_t k4a_device_get_installed_count(void)
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;
color_cb_streaming_capture_t color_capture_ready;

Просмотреть файл

@ -25,6 +25,7 @@ typedef struct _tewrapper_context_t
COND_HANDLE main_condition;
LOCK_HANDLE worker_lock;
COND_HANDLE worker_condition;
volatile bool thread_started;
volatile bool thread_stop;
k4a_result_t thread_start_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
// startup status.
Lock(tewrapper->main_lock);
tewrapper->thread_started = true;
tewrapper->thread_start_result = result;
Condition_Post(tewrapper->main_condition);
Unlock(tewrapper->main_lock);
@ -212,6 +214,7 @@ tewrapper_t tewrapper_create(k4a_transform_engine_calibration_t *transform_engin
{
bool locked = false;
tewrapper->thread_stop = false;
tewrapper->thread_started = false;
THREADAPI_RESULT tresult = ThreadAPI_Create(&tewrapper->thread, transform_engine_thread, tewrapper);
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);
locked = true;
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 (!tewrapper->thread_started)
{
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))

Просмотреть файл

@ -66,7 +66,7 @@ static bool transformation_compare_image_descriptors(const k4a_transformation_im
descriptor1->height_pixels != descriptor2->height_pixels ||
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.",
descriptor1->width_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,
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));
return K4A_RESULT_SUCCEEDED;
@ -459,7 +459,7 @@ static k4a_result_t transformation_depth_to_color(k4a_transformation_rgbz_contex
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_transformation_xy_tables_t *xy_tables_depth_camera,
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,
&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)
{
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_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;
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;
}
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_transformation_xy_tables_t *xy_tables_depth_camera,
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,
&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)
{
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_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;
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;
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++)
{
int16_t z = (int16_t)depth_image_data_uint16[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));
float x_tab = xy_tables->x_table[i];
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 + 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
__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++)
{
__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_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_hi = _mm_cvtps_epi32(_mm_mul_ps(depth_hi, *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_tab_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);
__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 ||
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)
{
LOG_ERROR("XYZ image data is null.", 0);

Просмотреть файл

@ -7,6 +7,7 @@
// System dependencies
#include <stdlib.h>
#include <math.h>
k4a_result_t transformation_get_mode_specific_calibration(const k4a_calibration_camera_t *depth_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)
{
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;
}
else
@ -467,6 +470,18 @@ transformation_depth_image_to_color_camera(k4a_transformation_t transformation_h
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 *
depth_image_descriptor->height_pixels);
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 (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 *
depth_image_descriptor->height_pixels);
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>
target_include_directories(k4a_usb_cmd PUBLIC
target_include_directories(k4a_usb_cmd PUBLIC
${K4A_PRIV_INCLUDE_DIR})
find_package(LibUSB REQUIRED)
# Dependencies of this library
target_link_libraries(k4a_usb_cmd PUBLIC
target_link_libraries(k4a_usb_cmd PUBLIC
azure::aziotsharedutil
LibUSB::LibUSB
k4ainternal::allocator

Просмотреть файл

@ -27,8 +27,7 @@ TraceLibUsbError(int err, const char *szCall, const char *szFile, int line, cons
// Example print:
// depth.cpp (86): allocator_create(&depth->allocator) returned ERROR_NOT_FOUND in depth_create
LOG_ERROR(
LOGGER_K4A, "%s (%d): %s returned %s in %s ", szFile, line, szCall, libusb_error_name(err), szFunction);
LOG_ERROR("%s (%d): %s returned %s in %s ", szFile, line, szCall, libusb_error_name(err), szFunction);
result = K4A_RESULT_FAILED;
}

Просмотреть файл

@ -11,6 +11,7 @@ add_subdirectory(DepthTests)
add_subdirectory(example)
add_subdirectory(ExternLibraries)
add_subdirectory(FirmwareTests)
add_subdirectory(logging)
add_subdirectory(IMUTests)
add_subdirectory(multidevice)
add_subdirectory(projections)

Просмотреть файл

@ -18,8 +18,9 @@ protected:
{
ASSERT_EQ(K4A_RESULT_SUCCEEDED, TRACE_CALL(setup_common_test()));
const ::testing::TestInfo *const test_info = ::testing::UnitTest::GetInstance()->current_test_info();
LOG_INFO("Test %s requires a connection exerciser.", test_info->name());
LOG_INFO("Disconnecting the device", 0);
printf("\nStarting test %s. This requires a connection exerciser to be connected.\n", test_info->name());
LOG_INFO("Starting test %s.", test_info->name());
LOG_INFO("Disconnecting the device");
g_connection_exerciser->set_usb_port(0);
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.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;
usb_cmd_get_device_count(&device_count);
ASSERT_EQ((uint32_t)0, device_count);
@ -78,7 +79,8 @@ protected:
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))
{
@ -108,7 +110,7 @@ protected:
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))
{
ThreadAPI_Sleep(1000);
@ -220,7 +222,7 @@ TEST_F(firmware_fw, DISABLED_update_timing)
ASSERT_EQ(K4A_RESULT_SUCCEEDED,
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,
perform_device_update(&firmware_handle,
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));
LOG_INFO("Updating the device to the Test firmware.");
printf("\n == Updating the device to the Test firmware.\n");
ASSERT_EQ(K4A_RESULT_SUCCEEDED,
perform_device_update(&firmware_handle,
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);
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, open_firmware_device(&firmware_handle));
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,
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,
perform_device_update(&firmware_handle,
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_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,
perform_device_update(&firmware_handle,
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_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,
perform_device_update(&firmware_handle,
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);
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, open_firmware_device(&firmware_handle));
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,
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,
perform_device_update(&firmware_handle,
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_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,
perform_device_update(&firmware_handle,
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_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,
perform_device_update(&firmware_handle,
g_test_firmware_buffer,

Просмотреть файл

@ -47,23 +47,25 @@
return K4A_RESULT_FAILED; \
}
char *g_candidate_firmware_path = nullptr;
int g_k4a_port_number = -1;
connection_exerciser *g_connection_exerciser = 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_candidate_firmware_path = nullptr;
uint8_t *g_candidate_firmware_buffer = nullptr;
size_t g_candidate_firmware_size = 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;
size_t g_lkg_firmware_size = 0;
firmware_package_info_t g_lkg_firmware_package_info = { 0 };
char *g_factory_firmware_path = nullptr;
uint8_t *g_factory_firmware_buffer = nullptr;
size_t g_factory_firmware_size = 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_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());
LOG_INFO("Clearing port...");
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)
{
@ -123,30 +125,30 @@ k4a_result_t setup_common_test()
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)
{
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;
}
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;
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);
std::cout << "Loading LKG firmware package: " << K4A_LKG_FIRMWARE_PATH << std::endl;
load_firmware_files(K4A_LKG_FIRMWARE_PATH, &g_lkg_firmware_buffer, &g_lkg_firmware_size);
std::cout << "Loading Test firmware package: " << g_test_firmware_path << std::endl;
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);
std::cout << "Loading Factory firmware package: " << K4A_FACTORY_FIRMWARE_PATH << std::endl;
load_firmware_files(K4A_FACTORY_FIRMWARE_PATH, &g_factory_firmware_buffer, &g_factory_firmware_size);
std::cout << "Loading Factory firmware package: " << g_factory_firmware_path << std::endl;
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);
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);
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;
}
@ -215,17 +217,17 @@ k4a_result_t load_firmware_files(char *firmware_path, uint8_t **firmware_buffer,
tempFirmwareBuffer = (uint8_t *)malloc(tempFirmwareSize);
if (tempFirmwareBuffer == NULL)
{
std::cout << "ERROR: Failed to allocate memory." << std::endl;
LOG_ERROR("Failed to allocate memory.", 0);
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);
fclose(pFirmwareFile);
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
{
@ -237,7 +239,7 @@ k4a_result_t load_firmware_files(char *firmware_path, uint8_t **firmware_buffer,
}
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)
@ -477,8 +479,12 @@ k4a_result_t open_firmware_device(firmware_t *firmware_handle)
while (device_count == 0 && retry++ < 20)
{
ThreadAPI_Sleep(500);
K4A_TEST_VERIFY_SUCCEEDED(usb_cmd_get_device_count(&device_count));
if (device_count == 0)
{
ThreadAPI_Sleep(500);
}
}
K4A_TEST_VERIFY_LE(retry, 20);
@ -498,6 +504,8 @@ k4a_result_t reset_device(firmware_t *firmware_handle)
firmware_destroy(*firmware_handle);
*firmware_handle = nullptr;
ThreadAPI_Sleep(1000);
// Re-open the device to ensure it is ready for use.
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));
ThreadAPI_Sleep(500);
ThreadAPI_Sleep(1000);
K4A_TEST_VERIFY_SUCCEEDED(g_connection_exerciser->set_usb_port(g_k4a_port_number));
firmware_destroy(*firmware_handle);
*firmware_handle = nullptr;
ThreadAPI_Sleep(1000);
// Re-open the device to ensure it is ready for use.
return TRACE_CALL(open_firmware_device(firmware_handle));
}
@ -788,16 +798,58 @@ int main(int argc, char **argv)
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)
{
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
{
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;
}
}
@ -812,8 +864,17 @@ int main(int argc, char **argv)
if (error)
{
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(" -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
}

Просмотреть файл

@ -11,19 +11,6 @@
#define UPDATE_TIMEOUT_MS 10 * 60 * 1000 // 10 Minutes should be way more than enough.
#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
{
FIRMWARE_OPERATION_START,
@ -45,18 +32,28 @@ typedef enum
extern int g_k4a_port_number;
extern connection_exerciser *g_connection_exerciser;
extern uint8_t *g_test_firmware_buffer;
extern size_t g_test_firmware_size;
extern firmware_package_info_t g_test_firmware_package_info;
// This will define the information about the relevant firmware packages to use in testing the 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.
// Candidate firmware - This is the firmware that is being validated.
extern uint8_t *g_candidate_firmware_buffer;
extern size_t g_candidate_firmware_size;
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 size_t g_lkg_firmware_size;
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 size_t g_factory_firmware_size;
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()));
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);
g_connection_exerciser->set_usb_port(0);
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.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;
usb_cmd_get_device_count(&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_status_summary_t finalStatus;
LOG_INFO("Beginning the \'%s\' test. Stage: %d Interruption: %d",
parameters.test_name,
parameters.component,
parameters.interruption);
printf("Beginning the \'%s\' test. Stage: %d Interruption: %d",
parameters.test_name,
parameters.component,
parameters.interruption);
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, open_firmware_device(&firmware_handle));
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));
// 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,
perform_device_update(&firmware_handle,
g_candidate_firmware_buffer,
@ -111,7 +111,7 @@ TEST_P(firmware_interrupt_fw, interrupt_update)
false));
// 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".
printf("Test ");
@ -190,7 +190,7 @@ TEST_P(firmware_interrupt_fw, interrupt_update)
// happened.
if (!compare_version(current_version.depth, { 0 }))
{
printf(" The Depth version was not expected\n");
printf(" ** The Depth version was not expected\n");
}
break;
@ -205,15 +205,15 @@ TEST_P(firmware_interrupt_fw, interrupt_update)
// reset actually happened.
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))
{
printf(" The Depth version was not expected\n");
printf(" ** The Depth version was not expected\n");
}
if (!compare_version(current_version.rgb, { 0 }))
{
printf(" The RGB version was not expected\n");
printf(" ** The RGB version was not expected\n");
}
break;
@ -222,7 +222,7 @@ TEST_P(firmware_interrupt_fw, interrupt_update)
}
// 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,
perform_device_update(&firmware_handle,
g_lkg_firmware_buffer,

Просмотреть файл

@ -36,7 +36,7 @@ TEST_F(playback_perf, test_open)
k4a_record_configuration_t config;
result = k4a_playback_get_record_configuration(handle, &config);
ASSERT_EQ(result, K4A_RESULT_SUCCEEDED);
std::cout << "Config:" << std::endl;
std::cout << "Record config:" << std::endl;
std::cout << " Tracks enabled:";
static const std::pair<bool *, std::string> tracks[] = { { &config.color_track_enabled, "Color" },
{ &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 << " 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);
}
@ -77,12 +129,38 @@ TEST_F(playback_perf, test_1000_reads_forward)
for (int i = 0; i < 1000; i++)
{
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);
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);
}
@ -109,12 +187,38 @@ TEST_F(playback_perf, test_1000_reads_backward)
for (int i = 0; i < 1000; i++)
{
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);
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);
}
@ -141,7 +245,12 @@ TEST_F(playback_perf, test_read_latency_30fps)
playback_result = k4a_playback_get_next_capture(handle, &capture);
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);
k4a_capture_release(capture);
@ -156,9 +265,65 @@ TEST_F(playback_perf, test_read_latency_30fps)
{
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;
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);
}
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);
}

Просмотреть файл

@ -29,6 +29,12 @@ protected:
void TearDown() override
{
for (cluster_t *cluster : *context->pending_clusters)
{
delete cluster;
}
context->pending_clusters->clear();
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
// 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)
{
ASSERT_EQ(check_sum, reference_val);
@ -364,19 +364,8 @@ TEST_F(transformation_ut, transformation_depth_image_to_point_cloud)
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_height_pixels = 576;
k4a_image_t depth_image = NULL;
@ -387,8 +376,8 @@ TEST_F(transformation_ut, transformation_create_depth_only)
&depth_image),
K4A_WAIT_RESULT_SUCCEEDED);
int color_image_width_pixels = 1920;
int color_image_height_pixels = 1080;
int color_image_width_pixels = 1280;
int color_image_height_pixels = 720;
k4a_image_t color_image = NULL;
ASSERT_EQ(image_create(K4A_IMAGE_FORMAT_DEPTH16,
color_image_width_pixels,
@ -413,151 +402,142 @@ TEST_F(transformation_ut, transformation_create_depth_only)
&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 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;
k4a_image_t xyz_depth_image = NULL;
ASSERT_EQ(image_create(K4A_IMAGE_FORMAT_CUSTOM,
depth_image_width_pixels,
depth_image_height_pixels,
depth_image_width_pixels * 3 * (int)sizeof(int16_t),
&point_cloud_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),
&xyz_depth_image),
K4A_RESULT_SUCCEEDED);
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);
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,
k4a_image_t xyz_color_image = NULL;
ASSERT_EQ(image_create(K4A_IMAGE_FORMAT_CUSTOM,
color_image_width_pixels,
color_image_height_pixels,
color_image_width_pixels * 4 * (int)sizeof(uint8_t),
&color_image),
K4A_WAIT_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);
color_image_width_pixels * 3 * (int)sizeof(int16_t),
&xyz_color_image),
K4A_RESULT_SUCCEEDED);
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 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);
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);
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);
uint8_t *transformed_color_image_buffer = image_get_buffer(transformed_color_image);
uint8_t *xyz_depth_image_buffer = image_get_buffer(xyz_depth_image);
uint8_t *xyz_color_image_buffer = image_get_buffer(xyz_color_image);
ASSERT_EQ(calibration.depth_camera_calibration.resolution_width, 0);
ASSERT_EQ(calibration.depth_camera_calibration.resolution_width, 0);
for (int i = 0; i < 5; i++)
{
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(color_image);
image_dec_ref(transformed_color_image);
image_dec_ref(transformed_depth_image);
transformation_destroy(transformation_handle);
image_dec_ref(color_image);
image_dec_ref(depth_image);
image_dec_ref(transformed_color_image);
image_dec_ref(transformed_depth_image);
image_dec_ref(xyz_color_image);
image_dec_ref(xyz_depth_image);
}
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
k4afilepicker.cpp
k4aimguiextensions.cpp
k4aimudatagraph.cpp
k4aimusamplesource.cpp
k4aimugraph.cpp
k4aimugraphdatagenerator.cpp
k4aimuwindow.cpp
k4alogdockcontrol.cpp
k4amicrophone.cpp
k4amicrophonelistener.cpp
k4apointcloudrenderer.cpp
@ -26,6 +27,7 @@ set(SOURCE_FILES
k4avideowindow.cpp
k4aviewer.cpp
k4aviewerimage.cpp
k4aviewerlogmanager.cpp
k4aviewererrormanager.cpp
k4aviewersettingsmanager.cpp
k4awindowmanager.cpp
@ -46,7 +48,6 @@ configure_file(
)
find_package(OpenGL REQUIRED)
find_package(LibUSB REQUIRED)
include_directories(
${OPENGL_INCLUDE_DIRS}
${CMAKE_CURRENT_LIST_DIR}

Просмотреть файл

@ -22,6 +22,7 @@ template<typename NotificationType> class IK4AObserver
public:
virtual void NotifyData(const NotificationType &data) = 0;
virtual void NotifyTermination() = 0;
virtual void ClearData() = 0;
virtual ~IK4AObserver() = default;

Просмотреть файл

@ -22,7 +22,6 @@
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdocumentation"
#pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
#pragma clang diagnostic ignored "-Wdouble-promotion"
#endif
#include "turbojpeg.h"

Просмотреть файл

@ -79,6 +79,14 @@ public:
m_failed = true;
}
void ClearData() override
{
std::lock_guard<std::mutex> lock(m_mutex);
m_textureBuffers.Clear();
m_inputImageBuffer.Clear();
}
~K4AConvertingImageSourceImpl() override
{
m_workerThreadShouldExit = true;

Просмотреть файл

@ -71,8 +71,8 @@ public:
void NotifyTermination()
{
std::lock_guard<std::mutex> lock(m_mutex);
m_primed = false;
m_mostRecentData = T();
for (const auto &wpObserver : m_observers)
{
if (auto spObserver = wpObserver.lock())
@ -84,6 +84,28 @@ public:
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:
std::list<std::weak_ptr<IK4AObserver<T>>> m_observers;

Просмотреть файл

@ -31,20 +31,41 @@ namespace
constexpr std::chrono::milliseconds CameraPollingTimeout(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>
bool PollSensor(const char *sensorFriendlyName,
k4a::device *device,
K4ADataSource<T> *dataSource,
bool *paused,
std::function<bool(k4a::device *, T *)> pollFn,
std::function<void(k4a::device *)> stopFn)
bool *started,
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
{
T data;
const bool succeeded = pollFn(device, &data);
const bool succeeded = pollFn(device, &data, timeout);
if (succeeded)
{
if (!*paused)
@ -54,31 +75,59 @@ bool PollSensor(const char *sensorFriendlyName,
return true;
}
// We've timed out.
//
failureWasTimeout = true;
errorMessage = "timed out!";
}
catch (const k4a::error &)
catch (const k4a::error &e)
{
failureWasTimeout = false;
errorMessage = e.what();
}
std::stringstream errorBuilder;
errorBuilder << sensorFriendlyName;
if (failureWasTimeout)
StopSensor(device, stopFn, dataSource, started);
if (!*abortInProgress)
{
errorBuilder << " timed out!";
}
else
{
errorBuilder << " failed!";
std::stringstream errorBuilder;
errorBuilder << sensorFriendlyName << " failed: " << errorMessage;
K4AViewerErrorManager::Instance().SetErrorStatus(errorBuilder.str());
}
K4AViewerErrorManager::Instance().SetErrorStatus(errorBuilder.str());
dataSource->NotifyTermination();
stopFn(device);
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
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 };
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 };
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
//
static_assert(sizeof(m_colorSettingsCache) == sizeof(ColorSetting) * 10,
static_assert(sizeof(m_colorSettingsCache) == sizeof(ColorSetting) * 9,
"Missing color setting in LoadColorSettingsCache()");
ReadColorSetting(K4A_COLOR_CONTROL_EXPOSURE_TIME_ABSOLUTE, &m_colorSettingsCache.ExposureTimeUs);
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_CONTRAST, &m_colorSettingsCache.Contrast);
ReadColorSetting(K4A_COLOR_CONTROL_SATURATION, &m_colorSettingsCache.Saturation);
@ -523,13 +568,6 @@ K4ADockControlStatus K4ADeviceDockControl::Show()
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,
[](ColorSetting *cacheEntry) {
return ImGui::Checkbox("Backlight Compensation", reinterpret_cast<bool *>(&cacheEntry->Value)) ?
@ -755,6 +793,16 @@ K4ADockControlStatus K4ADeviceDockControl::Show()
{
ImGuiExtensions::ButtonColorChanger colorChanger(ImGuiExtensions::ButtonColor::Green);
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))
{
Start();
@ -805,11 +853,11 @@ void K4ADeviceDockControl::Start()
const bool enableCameras = m_config.EnableColorCamera || m_config.EnableDepthCamera;
if (enableCameras)
{
StartCameras();
}
if (m_config.EnableImu)
{
StartImu();
bool camerasStarted = StartCameras();
if (camerasStarted && m_config.EnableImu)
{
StartImu();
}
}
if (m_config.EnableMicrophone)
{
@ -855,20 +903,32 @@ bool K4ADeviceDockControl::StartCameras()
K4ADataSource<k4a::capture> *pCameraDataSource = &m_cameraDataSource;
bool *pPaused = &m_paused;
bool *pCamerasStarted = &m_camerasStarted;
bool *pAbortInProgress = &m_camerasAbortInProgress;
bool isSubordinate = m_config.WiredSyncMode == K4A_WIRED_SYNC_MODE_SUBORDINATE;
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",
pDevice,
pCameraDataSource,
pPaused,
[](k4a::device *device, k4a::capture *capture) {
return device->get_capture(capture, CameraPollingTimeout);
pCamerasStarted,
pAbortInProgress,
[](k4a::device *device,
k4a::capture *capture,
std::chrono::milliseconds timeout) {
return device->get_capture(capture, timeout);
},
[pCamerasStarted](k4a::device *device) {
device->stop_cameras();
*pCamerasStarted = false;
});
[](k4a::device *device) { device->stop_cameras(); },
pollingTimeout);
});
return true;
@ -876,13 +936,12 @@ bool K4ADeviceDockControl::StartCameras()
void K4ADeviceDockControl::StopCameras()
{
if (m_cameraPollingThread)
{
m_cameraPollingThread.reset();
}
m_cameraDataSource.NotifyTermination();
m_device.stop_cameras();
m_camerasStarted = false;
StopPollingThread(&m_cameraPollingThread,
&m_device,
[](k4a::device *device) { device->stop_cameras(); },
&m_cameraDataSource,
&m_camerasStarted,
&m_camerasAbortInProgress);
}
bool K4ADeviceDockControl::StartMicrophone()
@ -943,34 +1002,45 @@ bool K4ADeviceDockControl::StartImu()
K4ADataSource<k4a_imu_sample_t> *pImuDataSource = &m_imuDataSource;
bool *pPaused = &m_paused;
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]() {
return PollSensor<k4a_imu_sample_t>("IMU",
pDevice,
pImuDataSource,
pPaused,
[](k4a::device *device, k4a_imu_sample_t *sample) {
return device->get_imu_sample(sample, ImuPollingTimeout);
},
[pImuStarted](k4a::device *device) {
device->stop_imu();
*pImuStarted = false;
});
});
m_imuPollingThread = std14::make_unique<K4APollingThread>(
[pDevice, pImuDataSource, pPaused, pImuStarted, pAbortInProgress, isSubordinate](bool firstRun) {
std::chrono::milliseconds pollingTimeout = ImuPollingTimeout;
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_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;
}
void K4ADeviceDockControl::StopImu()
{
if (m_imuPollingThread)
{
m_imuPollingThread.reset();
}
m_imuDataSource.NotifyTermination();
m_imuStarted = false;
m_device.stop_imu();
StopPollingThread(&m_imuPollingThread,
&m_device,
[](k4a::device *device) { device->stop_imu(); },
&m_imuDataSource,
&m_imuStarted,
&m_imuAbortInProgress);
}
void K4ADeviceDockControl::SetViewType(K4AWindowSet::ViewType viewType)

Просмотреть файл

@ -47,7 +47,6 @@ private:
{
ColorSetting ExposureTimeUs;
ColorSetting WhiteBalance;
ColorSetting AutoExposurePriority;
ColorSetting Brightness;
ColorSetting Contrast;
ColorSetting Saturation;
@ -107,6 +106,9 @@ private:
bool m_camerasStarted = false;
bool m_imuStarted = false;
bool m_camerasAbortInProgress = false;
bool m_imuAbortInProgress = false;
std::string m_deviceSerialNumber;
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)
{
k4a::image img = capture.get_color_image();
if (img.get_format() != T)
if (!img || img.get_format() != T)
{
return k4a::image();
}

Просмотреть файл

@ -29,7 +29,6 @@
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdocumentation"
#pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
#pragma clang diagnostic ignored "-Wdouble-promotion"
#endif
#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
//
#include "k4aimudatagraph.h"
#include "k4aimugraph.h"
// System headers
//
@ -41,15 +41,14 @@ constexpr float MinHeight = 50.f;
} // namespace
K4AImuDataGraph::K4AImuDataGraph(std::string &&title,
std::string &&xLabel,
std::string &&yLabel,
std::string &&zLabel,
std::string &&units,
const float minRange,
const float maxRange,
const float defaultRange,
const float scaleFactor) :
K4AImuGraph::K4AImuGraph(std::string &&title,
std::string &&xLabel,
std::string &&yLabel,
std::string &&zLabel,
std::string &&units,
const float minRange,
const float maxRange,
const float defaultRange) :
m_title(std::move(title)),
m_xLabel(std::move(xLabel)),
m_yLabel(std::move(yLabel)),
@ -58,38 +57,14 @@ K4AImuDataGraph::K4AImuDataGraph(std::string &&title,
m_minRange(minRange),
m_maxRange(maxRange),
m_currentRange(-defaultRange),
m_scaleFactor(scaleFactor),
m_scaleTitle(GetScaleTitle(m_title))
{
}
void K4AImuDataGraph::AddSample(const k4a_float3_t &sample, const uint64_t timestampUs)
{
// We want to average a few samples together to slow down the graph enough that it's readable
//
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)
void K4AImuGraph::Show(ImVec2 maxSize,
const K4AImuGraphData::AccumulatorArray &graphData,
int graphFrontIdx,
uint64_t 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::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
// grow when you drag up, which is a bit more intuitive.
@ -130,22 +105,26 @@ void K4AImuDataGraph::Show(ImVec2 maxSize)
ImGui::SameLine();
ImGui::BeginGroup();
PlotGraph(m_xLabel.c_str(), m_x, graphSize);
PlotGraph(m_yLabel.c_str(), m_y, graphSize);
PlotGraph(m_zLabel.c_str(), m_z, graphSize);
PlotGraph(m_xLabel.c_str(), graphSize, graphData, graphFrontIdx, 0);
PlotGraph(m_yLabel.c_str(), graphSize, graphData, graphFrontIdx, 1);
PlotGraph(m_zLabel.c_str(), graphSize, graphData, graphFrontIdx, 2);
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;
nameBuilder << "##" << name;
const float currentData = data[m_offset];
const float currentData = graphData[static_cast<size_t>(graphFrontIdx)].v[offset];
std::string label;
if (K4AViewerSettingsManager::Instance().GetShowInfoPane())
if (K4AViewerSettingsManager::Instance().GetViewerOption(ViewerOption::ShowInfoPane))
{
std::stringstream labelBuilder;
labelBuilder << name << ": ";
@ -175,10 +154,23 @@ void K4AImuDataGraph::PlotGraph(const char *name, const std::array<float, GraphS
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(),
&data[0],
static_cast<int>(data.size()),
static_cast<int>(m_offset),
[](void *data, int idx) {
auto *sd = reinterpret_cast<SelectorData *>(data);
return sd->data[idx].v[sd->offset];
},
&selectorData,
static_cast<int>(graphData.size()),
graphFrontIdx,
label.c_str(),
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
//
#include <cmath>
#include <utility>
// Library headers
@ -15,7 +16,6 @@
// Project headers
//
#include "k4aimudatagraph.h"
#include "k4aviewererrormanager.h"
#include "k4awindowsizehelpers.h"
@ -26,59 +26,35 @@ namespace
constexpr float AccelMinRange = 5.0f;
constexpr float AccelMaxRange = 100.0f;
constexpr float AccelDefaultRange = 20.0f;
constexpr float AccelScaleFactor = 1.0f;
constexpr float GyroMinRange = 5.0f;
constexpr float GyroMaxRange = 50.0f;
constexpr float GyroDefaultRange = 20.0f;
constexpr float GyroScaleFactor = 1.0f;
} // namespace
K4AImuWindow::K4AImuWindow(std::string &&title, std::shared_ptr<K4AImuSampleSource> sampleSource) :
m_sampleSource(std::move(sampleSource)),
K4AImuWindow::K4AImuWindow(std::string &&title, std::shared_ptr<K4AImuGraphDataGenerator> graphDataGenerator) :
m_graphDataGenerator(std::move(graphDataGenerator)),
m_title(std::move(title)),
m_accelerometerGraph("Accelerometer",
"X",
"Y",
"Z",
"m/s/s",
AccelMinRange,
AccelMaxRange,
AccelDefaultRange,
AccelScaleFactor),
m_gyroscopeGraph("Gyroscope",
" Roll",
"Pitch",
" Yaw",
"Rad/s",
GyroMinRange,
GyroMaxRange,
GyroDefaultRange,
GyroScaleFactor)
m_accGraph("Accelerometer", "X", "Y", "Z", "m/s/s", AccelMinRange, AccelMaxRange, AccelDefaultRange),
m_gyroGraph("Gyroscope", " Roll", "Pitch", " Yaw", "Rad/s", GyroMinRange, GyroMaxRange, GyroDefaultRange)
{
}
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;
}
if (m_failed)
{
ImGui::Text("Sample source failed!");
ImGui::Text("Data source failed!");
return;
}
k4a_imu_sample_t sample;
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);
}
K4AImuGraphDataGenerator::GraphReader reader = m_graphDataGenerator->GetGraphData();
// Sizing math
//
@ -100,14 +76,18 @@ void K4AImuWindow::Show(K4AWindowPlacementInfo placementInfo)
// Actually draw the widgets
//
m_accelerometerGraph.Show(graphSize);
m_accGraph.Show(graphSize, reader.Data->AccData, reader.Data->StartOffset, reader.Data->AccTimestamp);
ImGui::Separator();
m_gyroscopeGraph.Show(graphSize);
m_gyroGraph.Show(graphSize, reader.Data->GyroData, reader.Data->StartOffset, reader.Data->GyroTimestamp);
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

Просмотреть файл

@ -14,9 +14,9 @@
// Project headers
//
#include "k4aimusamplesource.h"
#include "ik4avisualizationwindow.h"
#include "k4aimudatagraph.h"
#include "k4aimugraphdatagenerator.h"
#include "k4aimugraph.h"
namespace k4aviewer
{
@ -27,7 +27,7 @@ public:
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(const K4AImuWindow &) = delete;
@ -36,13 +36,13 @@ public:
K4AImuWindow &operator=(const K4AImuWindow &&) = delete;
private:
std::shared_ptr<K4AImuSampleSource> m_sampleSource;
std::shared_ptr<K4AImuGraphDataGenerator> m_graphDataGenerator;
std::string m_title;
bool m_failed = false;
K4AImuDataGraph m_accelerometerGraph;
K4AImuDataGraph m_gyroscopeGraph;
double m_sensorTemperature;
K4AImuGraph m_accGraph;
K4AImuGraph m_gyroGraph;
};
} // 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
{
m_failed = true;

Просмотреть файл

@ -188,7 +188,11 @@ K4APointCloudVisualizer::K4APointCloudVisualizer(const bool enableColorPointClou
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);
SetColorizationStrategy(m_colorizationStrategy);

Просмотреть файл

@ -18,6 +18,7 @@
//
#include "k4aimguiextensions.h"
#include "k4aviewererrormanager.h"
#include "k4aviewerlogmanager.h"
#include "k4awindowsizehelpers.h"
using namespace k4aviewer;
@ -67,28 +68,6 @@ void K4APointCloudWindow::Show(K4AWindowPlacementInfo placementInfo)
const ImVec2 imageStartPos = ImGui::GetCursorScreenPos();
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);
bool colorizationStrategyUpdated = false;
@ -172,30 +151,48 @@ K4APointCloudWindow::K4APointCloudWindow(std::string &&windowTitle,
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];
const bool rightMouseDown = io.MouseDown[GLFW_MOUSE_BUTTON_2];
if (ImGui::IsWindowHovered())
{
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 mouseDelta{ io.MouseDelta.x, io.MouseDelta.y };
const linmath::vec2 dimensions{ displayDimensions.x, displayDimensions.y };
MouseMovementType movementType = MouseMovementType::None;
if (leftMouseDown)
// Only count drags if they originated on the image
//
if (mouseDownPos.x >= 0.f && mouseDownPos.x <= displayDimensions.x && mouseDownPos.y >= 0.f &&
mouseDownPos.y <= displayDimensions.y)
{
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;
case PointCloudVisualizationResult::MissingDepthImage:
++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;
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_missingColorImages;
break;
case PointCloudVisualizationResult::OpenGlError:
SetFailed("OpenGL error!");

Просмотреть файл

@ -57,10 +57,6 @@ private:
static constexpr int MaxConsecutiveMissingImages = 10;
int m_consecutiveMissingImages = 0;
bool m_haveShownMissingImagesWarning = false;
int m_missingColorImages = 0;
int m_missingDepthImages = 0;
};
} // namespace k4aviewer

Просмотреть файл

@ -14,26 +14,37 @@
// Project headers
//
#include "k4aviewerutil.h"
namespace k4aviewer
{
class K4APollingThread
{
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);
}
void Stop()
{
m_shouldExit = true;
StopAsync();
if (m_thread.joinable())
{
m_thread.join();
}
}
void StopAsync()
{
m_shouldExit = true;
}
bool IsRunning() const
{
return m_isRunning;
}
~K4APollingThread()
{
Stop();
@ -42,19 +53,24 @@ public:
private:
static void Run(K4APollingThread *instance)
{
instance->m_isRunning = true;
CleanupGuard runGuard([instance]() { instance->m_isRunning = false; });
bool firstRun = true;
while (!instance->m_shouldExit)
{
if (!instance->m_pollFn())
if (!instance->m_pollFn(firstRun))
{
instance->m_shouldExit = true;
}
firstRun = false;
}
}
std::thread m_thread;
volatile bool m_shouldExit = false;
volatile bool m_isRunning = false;
std::function<bool()> m_pollFn;
std::function<bool(bool)> m_pollFn;
};
} // namespace k4aviewer

Просмотреть файл

@ -21,6 +21,7 @@
#include "k4atypeoperators.h"
#include "k4aviewerutil.h"
#include "k4awindowmanager.h"
#include "k4aimugraphdatagenerator.h"
using namespace k4aviewer;
namespace
@ -121,15 +122,15 @@ K4ARecordingDockControl::K4ARecordingDockControl(std::string &&path, k4a::playba
m_playbackThreadState.Recording = std::move(recording);
PlaybackThreadState *pThreadState = &m_playbackThreadState;
m_playbackThread = std::unique_ptr<K4APollingThread>(
new K4APollingThread([pThreadState]() { return PlaybackThreadFn(pThreadState); }));
m_playbackThread = std14::make_unique<K4APollingThread>(
[pThreadState](bool) { return PlaybackThreadFn(pThreadState); });
SetViewType(K4AWindowSet::ViewType::Normal);
}
K4ADockControlStatus K4ARecordingDockControl::Show()
{
ImGui::Text("%s", m_filenameLabel.c_str());
ImGui::TextUnformatted(m_filenameLabel.c_str());
ImGui::SameLine();
ImGuiExtensions::ButtonColorChanger cc(ImGuiExtensions::ButtonColor::Red);
if (ImGui::SmallButton("Close"))
@ -140,14 +141,15 @@ K4ADockControlStatus K4ARecordingDockControl::Show()
cc.Clear();
ImGui::Separator();
ImGui::Text("%s", "Image formats");
ImGui::TextUnformatted("Recording Settings");
ImGui::Text("FPS: %s", m_fpsLabel.c_str());
ImGui::Text("Depth mode: %s", m_depthModeLabel.c_str());
ImGui::Text("Color format: %s", m_colorFormatLabel.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::Text("%s", "Sync settings");
ImGui::TextUnformatted("Sync settings");
ImGui::Text("Depth/color delay (us): %d", m_depthDelayOffColorUsec);
ImGui::Text("Sync mode: %s", m_wiredSyncModeLabel.c_str());
ImGui::Text("Subordinate delay (us): %d", m_subordinateDelayOffMasterUsec);
@ -155,12 +157,18 @@ K4ADockControlStatus K4ARecordingDockControl::Show()
ImGui::Text("Recording Length (us): %lu", m_recordingLengthUsec);
ImGui::Separator();
ImGui::Text("%s", "Device info");
ImGui::TextUnformatted("Device info");
ImGui::Text("Device S/N: %s", m_deviceSerialNumber.c_str());
ImGui::Text("RGB camera FW: %s", m_colorFirmwareVersion.c_str());
ImGui::Text("Depth camera FW: %s", m_depthFirmwareVersion.c_str());
ImGui::Separator();
if (!m_playbackThread->IsRunning())
{
ImGui::Text("Playback failed!");
return K4ADockControlStatus::Ok;
}
if (ImGui::Button("<|"))
{
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);
bool forceRefreshImuData = false;
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->SeekTimestamp = InvalidSeekTime;
@ -248,6 +261,11 @@ bool K4ARecordingDockControl::PlaybackThreadFn(PlaybackThreadState *state)
backward = state->Step == StepDirection::Backward;
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
// under most circumstances. If the user stepped to the last
// 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->RecordingAtEnd = false;
forceRefreshImuData = true;
}
k4a::capture nextCapture;
@ -293,10 +312,77 @@ bool K4ARecordingDockControl::PlaybackThreadFn(PlaybackThreadState *state)
state->CurrentCaptureTimestamp = GetCaptureTimestamp(nextCapture);
// Update the images' timestamps using the timing data embedded in the recording
// so we show comparable timestamps whenplaying back synchronized recordings
// Read IMU data up to the next timestamp, if applicable
//
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(),
nextCapture.get_depth_image(),
nextCapture.get_ir_image() };
@ -309,7 +395,7 @@ bool K4ARecordingDockControl::PlaybackThreadFn(PlaybackThreadState *state)
}
}
state->DataSource.NotifyObservers(nextCapture);
state->CaptureDataSource.NotifyObservers(nextCapture);
lock.unlock();
// 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();
std::lock_guard<std::mutex> lock(m_playbackThreadState.Mutex);
K4ADataSource<k4a_imu_sample_t> *imuDataSource = nullptr;
switch (viewType)
{
case K4AWindowSet::ViewType::Normal:
if (m_recordConfiguration.imu_track_enabled)
{
m_playbackThreadState.ImuPlaybackEnabled = true;
imuDataSource = &m_playbackThreadState.ImuDataSource;
}
K4AWindowSet::StartNormalWindows(m_filenameLabel.c_str(),
&m_playbackThreadState.DataSource,
nullptr, // IMU playback not supported yet
&m_playbackThreadState.CaptureDataSource,
imuDataSource,
nullptr, // Audio source - sound is not supported in recordings
m_recordingHasDepth,
m_recordConfiguration.depth_mode,
@ -391,7 +484,7 @@ void K4ARecordingDockControl::SetViewType(K4AWindowSet::ViewType viewType)
m_recordConfiguration.color_format == K4A_IMAGE_FORMAT_COLOR_BGRA32;
K4AWindowSet::StartPointCloudWindow(m_filenameLabel.c_str(),
std::move(calibration),
&m_playbackThreadState.DataSource,
&m_playbackThreadState.CaptureDataSource,
colorPointCloudAvailable);
}
catch (const k4a::error &e)

Просмотреть файл

@ -18,6 +18,7 @@
#include "ik4adockcontrol.h"
#include "k4adatasource.h"
#include "k4aimugraphdatagenerator.h"
#include "k4apollingthread.h"
#include "k4awindowset.h"
@ -59,7 +60,10 @@ private:
// Recording state
//
k4a::playback Recording;
K4ADataSource<k4a::capture> DataSource;
K4ADataSource<k4a::capture> CaptureDataSource;
K4ADataSource<k4a_imu_sample_t> ImuDataSource;
bool ImuPlaybackEnabled;
} m_playbackThreadState;
static bool PlaybackThreadFn(PlaybackThreadState *state);

Просмотреть файл

@ -43,6 +43,13 @@ public:
return m_count == 0;
}
void Clear()
{
std::lock_guard<std::mutex> lock(m_mutex);
m_count = 0;
m_buffer.fill(T());
}
bool Full()
{
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:
return s << "EXPOSURE_TIME_ABSOLUTE";
case K4A_COLOR_CONTROL_AUTO_EXPOSURE_PRIORITY:
return s << "AUTO_EXPOSURE_PRIORITY";
case K4A_COLOR_CONTROL_BRIGHTNESS:
return s << "BRIGHTNESS";
case K4A_COLOR_CONTROL_CONTRAST:

Просмотреть файл

@ -85,7 +85,8 @@ public:
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::SetNextWindowPos(imageStartPos, ImGuiCond_Always);
@ -149,7 +150,7 @@ private:
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());
}

Просмотреть файл

@ -17,6 +17,7 @@
// Project headers
//
#include "k4aaudiomanager.h"
#include "k4alogdockcontrol.h"
#include "k4asourceselectiondockcontrol.h"
#include "k4aviewererrormanager.h"
#include "k4aviewerutil.h"
@ -88,7 +89,7 @@ void APIENTRY glDebugOutput(GLenum source,
}
#endif
K4AViewer::K4AViewer(const K4AViewerOptions &args)
K4AViewer::K4AViewer(const K4AViewerArgs &args)
{
// Setup window
glfwSetErrorCallback(LogGlfwError);
@ -145,6 +146,7 @@ K4AViewer::K4AViewer(const K4AViewerOptions &args)
}
K4AWindowManager::Instance().PushLeftDockControl(std14::make_unique<K4ASourceSelectionDockControl>());
K4AWindowManager::Instance().PushBottomDockControl(std14::make_unique<K4ALogDockControl>());
}
K4AViewer::~K4AViewer()
@ -222,35 +224,43 @@ void K4AViewer::ShowMainMenuBar()
{
if (ImGui::BeginMainMenuBar())
{
if (ImGui::BeginMenu("File"))
if (ImGui::BeginMenu("Settings"))
{
auto &settings = K4AViewerSettingsManager::Instance();
if (ImGui::MenuItem("Show info overlay", nullptr, settings.GetShowInfoPane()))
settings.SetShowInfoPane(!settings.GetShowInfoPane());
if (settings.GetShowInfoPane())
ShowViewerOptionMenuItem("Show log dock", ViewerOption::ShowLogDock);
ShowViewerOptionMenuItem("Show info overlay", ViewerOption::ShowInfoPane);
if (K4AViewerSettingsManager::Instance().GetViewerOption(ViewerOption::ShowInfoPane))
{
if (ImGui::MenuItem("Show framerate info", nullptr, settings.GetShowFrameRateInfo()))
settings.SetShowFrameRateInfo(!settings.GetShowFrameRateInfo());
ShowViewerOptionMenuItem("Show framerate", ViewerOption::ShowFrameRateInfo);
}
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"))
{
glfwSetWindowShouldClose(m_window, true);
}
ImGui::EndMenu();
}
if (m_showDeveloperOptions)
if (K4AViewerSettingsManager::Instance().GetViewerOption(ViewerOption::ShowDeveloperOptions))
{
if (ImGui::BeginMenu("Developer"))
{
if (ImGui::MenuItem("Show demo window", nullptr, m_showDemoWindow))
m_showDemoWindow = !m_showDemoWindow;
if (ImGui::MenuItem("Show style editor", nullptr, m_showStyleEditor))
m_showStyleEditor = !m_showStyleEditor;
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::MenuItem("Show demo window", nullptr, &m_showDemoWindow);
ImGui::MenuItem("Show style editor", nullptr, &m_showStyleEditor);
ImGui::MenuItem("Show metrics window", nullptr, &m_showMetricsWindow);
ImGui::MenuItem("Show perf counters", nullptr, &m_showPerfCounters);
ImGui::EndMenu();
}
}
@ -296,3 +306,14 @@ void K4AViewer::ShowErrorOverlay()
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
//
#include "k4aviewersettingsmanager.h"
struct K4AViewerOptions
struct K4AViewerArgs
{
bool HighDpi = false;
};
@ -25,7 +26,7 @@ namespace k4aviewer
class K4AViewer
{
public:
explicit K4AViewer(const K4AViewerOptions &args);
explicit K4AViewer(const K4AViewerArgs &args);
~K4AViewer();
void Run();
@ -42,8 +43,9 @@ private:
void ShowErrorOverlay();
void ShowViewerOptionMenuItem(const char *msg, ViewerOption option);
GLFWwindow *m_window;
bool m_showDeveloperOptions = false;
bool m_showDemoWindow = false;
bool m_showStyleEditor = false;
bool m_showMetricsWindow = false;

Просмотреть файл

@ -15,6 +15,7 @@
// Project headers
//
#include "k4atypeoperators.h"
#include "k4aviewersettingsmanager.h"
using namespace k4aviewer;
@ -37,6 +38,7 @@ void K4AViewerErrorManager::SetErrorStatus(const std::string &msg)
void K4AViewerErrorManager::SetErrorStatus(std::string &&msg)
{
K4AViewerSettingsManager::Instance().SetViewerOption(ViewerOption::ShowLogDock, true);
std::lock_guard<std::mutex> lock(m_mutex);
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)
{
K4AViewerSettingsManager::Instance().SetViewerOption(ViewerOption::ShowLogDock, true);
SetErrorStatus(msg.c_str(), result);
}

Просмотреть файл

@ -71,7 +71,14 @@ GLenum K4AViewerImage::UpdateTexture(const uint8_t *data)
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))
{
@ -111,15 +118,7 @@ GLenum K4AViewerImage::Create(std::shared_ptr<K4AViewerImage> *out,
static_cast<GLsizei>(dimensions.Width),
static_cast<GLsizei>(dimensions.Height));
GLenum status;
if (data)
{
status = newTexture->UpdateTexture(data);
}
else
{
status = glGetError();
}
GLenum status = newTexture->UpdateTexture(data);
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
//
#include "filesystem17.h"
#include "k4atypeoperators.h"
using namespace k4aviewer;
namespace
{
constexpr char SettingsFileName[] = "k4aviewersettings.txt";
constexpr char Separator[] = " ";
constexpr char ShowFramerateTag[] = "ShowFramerate";
constexpr char ShowInfoPaneTag[] = "ShowInfoPane";
} // namespace
namespace k4aviewer
@ -153,6 +149,128 @@ std::istream &operator>>(std::istream &s, K4ADeviceConfiguration &val)
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
// 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;
}
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()
{
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();
}
void K4AViewerSettingsManager::SaveSettings()
{
std::ofstream fileWriter(SettingsFileName);
std::ofstream fileWriter(m_settingsFilePath.c_str());
fileWriter << m_settingsPayload.SavedDeviceConfiguration << std::endl;
fileWriter << ShowFramerateTag << Separator << m_settingsPayload.ShowFrameRateInfo << std::endl;
fileWriter << ShowInfoPaneTag << Separator << m_settingsPayload.ShowInfoPane << std::endl;
fileWriter << m_settingsPayload.Options << std::endl;
}
void K4AViewerSettingsManager::LoadSettings()
{
std::ifstream fileReader(SettingsFileName);
std::ifstream fileReader(m_settingsFilePath.c_str());
if (!fileReader)
{
SetDefaults();
return;
}
bool readSettingsFailed = false;
SettingsPayload newSettingsPayload;
fileReader >> newSettingsPayload.SavedDeviceConfiguration;
bool readFailed = fileReader.fail();
while (!readFailed && !fileReader.eof())
{
std::string variableTag;
fileReader >> variableTag;
fileReader >> newSettingsPayload.Options;
if (variableTag == ShowFramerateTag)
{
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)
if (fileReader.fail())
{
// File is corrupt, try to delete it
//
readSettingsFailed = true;
fileReader.close();
(void)std::remove(SettingsFileName);
SetDefaults();
}
if (!readSettingsFailed)
else
{
m_settingsPayload = newSettingsPayload;
}
}
void K4AViewerSettingsManager::SetDefaults()
{
if (!m_settingsFilePath.empty())
{
(void)std::remove(m_settingsFilePath.c_str());
}
m_settingsPayload = SettingsPayload();
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше