diff --git a/docs/releasing.md b/docs/releasing.md
index 0d8457f91..65f021037 100644
--- a/docs/releasing.md
+++ b/docs/releasing.md
@@ -88,60 +88,63 @@ used to create a released build. Checkout the commit that matches the release la
## Artifacts and Packaging
The following table contains all artifacts that are released in installers and packages. The file paths listed in the table are only representative, within any given package they will be different to match the standards and conventions for those packages. Each unique released artifact is listed in this table only once, although they will potentially appear duplicate times in packages or installers.
-File | MSI/Installer | NuGet | Runtime Debian Package | Development Debian Package | Tooling Debian Package
--------------------------------------------------------------- | ------------------ | ------------------ | ------------------ | ------------------ | -----------------
-LICENSE.txt \* | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark:
-REDIST.txt \* | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark:
-ThirdPartyNotices.txt \* | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark:
-version.txt \* | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark:
-build/msbuild/native/Microsoft.Azure.Kinect.Sensor.targets \* | :one: | :white_check_mark: | | |
-build/msbuild/netstandard2.0/Microsoft.Azure.Kinect.Sensor.targets \* | :two: | :two: | | |
-build/cmake/x64/k4aConfig.cmake | :one: | :one: | | :white_check_mark: |
-build/cmake/x64/k4aConfigVersion.cmake | :one: | :one: | | :white_check_mark: |
-build/cmake/x64/k4aTargets-relwithdebinfo.cmake | :one: | :one: | | :white_check_mark: |
-build/cmake/x64/k4aTargets.cmake | :one: | :one: | | :white_check_mark: |
-build/cmake/x64/k4arecordConfig.cmake | :one: | :one: | | :white_check_mark: |
-build/cmake/x64/k4arecordConfigVersion.cmake | :one: | :one: | | :white_check_mark: |
-build/cmake/x64/k4arecordTargets-relwithdebinfo.cmake | :one: | :one: | | :white_check_mark: |
-build/cmake/x64/k4arecordTargets.cmake | :one: | :one: | | :white_check_mark: |
-include/k4a/k4a.h | :white_check_mark: | :white_check_mark: | | :white_check_mark: |
-include/k4a/k4a.hpp | :white_check_mark: | :white_check_mark: | | :white_check_mark: |
-include/k4a/k4a_export.h | :white_check_mark: | :white_check_mark: | | :white_check_mark: |
-include/k4a/k4atypes.h | :white_check_mark: | :white_check_mark: | | :white_check_mark: |
-include/k4a/k4aversion.h | :white_check_mark: | :white_check_mark: | | :white_check_mark: |
-include/k4arecord/k4arecord_export.h | :white_check_mark: | :white_check_mark: | | :white_check_mark: |
-include/k4arecord/playback.h | :white_check_mark: | :white_check_mark: | | :white_check_mark: |
-include/k4arecord/record.h | :white_check_mark: | :white_check_mark: | | :white_check_mark: |
-include/k4arecord/types.h | :white_check_mark: | :white_check_mark: | | :white_check_mark: |
-linux-ubuntu/x64/release/libdepthengine.so \* | | | :white_check_mark: | |
-linux-ubuntu/x64/release/libdepthengine.so.2.0 \* | | | :white_check_mark: | |
-linux-ubuntu/x64/release/libk4a.so (symlink) | | | | :white_check_mark: |
-linux-ubuntu/x64/release/libk4a.so.1.x (symlink) | | | :white_check_mark: | |
-linux-ubuntu/x64/release/libk4a.so.1.x.x | | | :white_check_mark: | |
-linux-ubuntu/x64/release/libk4arecord.so (symlink) | | | | :white_check_mark: |
-linux-ubuntu/x64/release/libk4arecord.so.1.x (symlink) | | | :white_check_mark: | |
-linux-ubuntu/x64/release/libk4arecord.so.1.x.x | | | :white_check_mark: | |
-linux-ubuntu/tools/x64/release/AzureKinectFirmwareTool | | | | | :white_check_mark:
-linux-ubuntu/tools/x64/release/k4arecorder | | | | | :white_check_mark:
-linux-ubuntu/tools/x64/release/k4aviewer | | | | | :white_check_mark:
-netstandard2.0/AnyCpu/release/Microsoft.AzureKinect.deps.json | :two: | :two: | | |
-netstandard2.0/AnyCpu/release/Microsoft.AzureKinect.dll | :two: | :two: | | |
-netstandard2.0/AnyCpu/release/Microsoft.AzureKinect.pdb | :two: | :two: | | |
-netstandard2.0/AnyCpu/release/Microsoft.AzureKinect.xml | :two: | :two: | | |
-windows-desktop/amd64/release/depthengine_2_0.dll \* | :white_check_mark: | :white_check_mark: | | |
-windows-desktop/amd64/release/k4a.dll | :white_check_mark: | :white_check_mark: | | |
-windows-desktop/amd64/release/k4a.lib | :white_check_mark: | :white_check_mark: | | |
-windows-desktop/amd64/release/k4a.pdb | :white_check_mark: | :white_check_mark: | | |
-windows-desktop/amd64/release/k4arecord.dll | :white_check_mark: | :white_check_mark: | | |
-windows-desktop/amd64/release/k4arecord.lib | :white_check_mark: | :white_check_mark: | | |
-windows-desktop/amd64/release/k4arecord.pdb | :white_check_mark: | :white_check_mark: | | |
-windows-desktop/tools/amd64/release/AzureKinectFirmwareTool.exe | :white_check_mark: | | | |
-windows-desktop/tools/amd64/release/AzureKinectFirmwareTool.pdb | :white_check_mark: | | | |
-windows-desktop/tools/amd64/release/k4arecorder.exe | :white_check_mark: | | | |
-windows-desktop/tools/amd64/release/k4arecorder.pdb | :white_check_mark: | | | |
-windows-desktop/tools/amd64/release/k4aviewer.exe | :white_check_mark: | | | |
-windows-desktop/tools/amd64/release/k4aviewer.pdb | :white_check_mark: | | | |
+File | MSI | NuGet | Runtime Debian Package | Development Debian Package | Tooling Debian Package
+----------------------------------------------------------------------------- | ------------------ | ------------------ | ------------------ | ------------------ | -----------------
+LICENSE.txt \* | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark:
+REDIST.txt \* | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark:
+ThirdPartyNotices.txt \* | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark:
+version.txt \* | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark:
+build/msbuild/native/Microsoft.Azure.Kinect.Sensor.targets \* | :one: | :white_check_mark: | | |
+build/msbuild/netstandard2.0/Microsoft.Azure.Kinect.Sensor.targets \* | :white_check_mark: | :white_check_mark: | | |
+build/cmake/x64/k4aConfig.cmake | :one: | :one: | | :white_check_mark: |
+build/cmake/x64/k4aConfigVersion.cmake | :one: | :one: | | :white_check_mark: |
+build/cmake/x64/k4aTargets-relwithdebinfo.cmake | :one: | :one: | | :white_check_mark: |
+build/cmake/x64/k4aTargets.cmake | :one: | :one: | | :white_check_mark: |
+build/cmake/x64/k4arecordConfig.cmake | :one: | :one: | | :white_check_mark: |
+build/cmake/x64/k4arecordConfigVersion.cmake | :one: | :one: | | :white_check_mark: |
+build/cmake/x64/k4arecordTargets-relwithdebinfo.cmake | :one: | :one: | | :white_check_mark: |
+build/cmake/x64/k4arecordTargets.cmake | :one: | :one: | | :white_check_mark: |
+include/k4a/k4a.h | :white_check_mark: | :white_check_mark: | | :white_check_mark: |
+include/k4a/k4a.hpp | :white_check_mark: | :white_check_mark: | | :white_check_mark: |
+include/k4a/k4a_export.h | :white_check_mark: | :white_check_mark: | | :white_check_mark: |
+include/k4a/k4atypes.h | :white_check_mark: | :white_check_mark: | | :white_check_mark: |
+include/k4a/k4aversion.h | :white_check_mark: | :white_check_mark: | | :white_check_mark: |
+include/k4arecord/k4arecord_export.h | :white_check_mark: | :white_check_mark: | | :white_check_mark: |
+include/k4arecord/playback.h | :white_check_mark: | :white_check_mark: | | :white_check_mark: |
+include/k4arecord/record.h | :white_check_mark: | :white_check_mark: | | :white_check_mark: |
+include/k4arecord/types.h | :white_check_mark: | :white_check_mark: | | :white_check_mark: |
+linux-ubuntu/x64/release/libdepthengine.so \* | | | :white_check_mark: | |
+linux-ubuntu/x64/release/libdepthengine.so.2.0 \* | | | :white_check_mark: | |
+linux-ubuntu/x64/release/libk4a.so (symlink) | | | | :white_check_mark: |
+linux-ubuntu/x64/release/libk4a.so.1.x (symlink) | | | :white_check_mark: | |
+linux-ubuntu/x64/release/libk4a.so.1.x.x | | | :white_check_mark: | |
+linux-ubuntu/x64/release/libk4arecord.so (symlink) | | | | :white_check_mark: |
+linux-ubuntu/x64/release/libk4arecord.so.1.x (symlink) | | | :white_check_mark: | |
+linux-ubuntu/x64/release/libk4arecord.so.1.x.x | | | :white_check_mark: | |
+linux-ubuntu/tools/x64/release/AzureKinectFirmwareTool | | | | | :white_check_mark:
+linux-ubuntu/tools/x64/release/k4arecorder | | | | | :white_check_mark:
+linux-ubuntu/tools/x64/release/k4aviewer | | | | | :white_check_mark:
+netstandard2.0/AnyCpu/release/Microsoft.Azure.Kinect.Sensor.deps.json | :white_check_mark: | :white_check_mark: | | |
+netstandard2.0/AnyCpu/release/Microsoft.Azure.Kinect.Sensor.dll | :white_check_mark: | :white_check_mark: | | |
+netstandard2.0/AnyCpu/release/Microsoft.Azure.Kinect.Sensor.pdb | :white_check_mark: | :white_check_mark: | | |
+netstandard2.0/AnyCpu/release/Microsoft.Azure.Kinect.Sensor.xml | :white_check_mark: | :white_check_mark: | | |
+netstandard2.0/AnyCpu/release/Microsoft.Azure.Kinect.Sensor.Record.deps.json | :white_check_mark: | :white_check_mark: | | |
+netstandard2.0/AnyCpu/release/Microsoft.Azure.Kinect.Sensor.Record.dll | :white_check_mark: | :white_check_mark: | | |
+netstandard2.0/AnyCpu/release/Microsoft.Azure.Kinect.Sensor.Record.pdb | :white_check_mark: | :white_check_mark: | | |
+netstandard2.0/AnyCpu/release/Microsoft.Azure.Kinect.Sensor.Record.xml | :white_check_mark: | :white_check_mark: | | |
+windows-desktop/amd64/release/depthengine_2_0.dll \* | :white_check_mark: | :white_check_mark: | | |
+windows-desktop/amd64/release/k4a.dll | :white_check_mark: | :white_check_mark: | | |
+windows-desktop/amd64/release/k4a.lib | :white_check_mark: | :white_check_mark: | | |
+windows-desktop/amd64/release/k4a.pdb | :white_check_mark: | :white_check_mark: | | |
+windows-desktop/amd64/release/k4arecord.dll | :white_check_mark: | :white_check_mark: | | |
+windows-desktop/amd64/release/k4arecord.lib | :white_check_mark: | :white_check_mark: | | |
+windows-desktop/amd64/release/k4arecord.pdb | :white_check_mark: | :white_check_mark: | | |
+windows-desktop/tools/amd64/release/AzureKinectFirmwareTool.exe | :white_check_mark: | | | |
+windows-desktop/tools/amd64/release/AzureKinectFirmwareTool.pdb | :white_check_mark: | | | |
+windows-desktop/tools/amd64/release/k4arecorder.exe | :white_check_mark: | | | |
+windows-desktop/tools/amd64/release/k4arecorder.pdb | :white_check_mark: | | | |
+windows-desktop/tools/amd64/release/k4aviewer.exe | :white_check_mark: | | | |
+windows-desktop/tools/amd64/release/k4aviewer.pdb | :white_check_mark: | | | |
* \* These files are generated/included at packaging time from Microsoft Internal sources.
* :one: Include CMake and MS Build files in the MSI (issue [#370](https://github.com/microsoft/Azure-Kinect-Sensor-SDK/issues/370))
-* :two: .NET support planned for a future release (issue [#136](https://github.com/microsoft/Azure-Kinect-Sensor-SDK/issues/136))
diff --git a/include/k4arecord/playback.h b/include/k4arecord/playback.h
index 6502ea8fa..b1f2d14e4 100644
--- a/include/k4arecord/playback.h
+++ b/include/k4arecord/playback.h
@@ -818,7 +818,7 @@ K4ARECORD_EXPORT void k4a_playback_data_block_release(k4a_playback_data_block_t
*
* \remarks
* The first call to k4a_playback_get_next_imu_sample() after k4a_playback_seek_timestamp() will return the first imu
- * sample with a timestamp greter than or equal to the seek time.
+ * sample with a timestamp greater than or equal to the seek time.
*
* \remarks
* The first call to k4a_playback_get_previous_imu_sample() after k4a_playback_seek_timestamp() will return the first
diff --git a/include/k4arecord/record.h b/include/k4arecord/record.h
index 998ce3d69..90d1ab654 100644
--- a/include/k4arecord/record.h
+++ b/include/k4arecord/record.h
@@ -462,6 +462,52 @@ K4ARECORD_EXPORT k4a_result_t k4a_record_flush(k4a_record_t recording_handle);
*/
K4ARECORD_EXPORT void k4a_record_close(k4a_record_t recording_handle);
+/** Sets and clears the callback function to receive debug messages from the Azure Kinect record and playback APIs.
+ *
+ * \param message_cb
+ * The callback function to receive 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, k4a_record_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, K4A_RECORD_ENABLE_LOG_TO_A_FILE, and K4A_LOG_LEVEL. However there is a slight change in
+ * default behavior when using this function. By default, when k4a_record_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 k4a_record_set_debug_message_handler registers a callback function before
+ * k4a_record_create() or k4a_playback_create() 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.
+ *
+ * \xmlonly
+ *
+ * k4a.h (include k4a/k4a.h)
+ * k4a.lib
+ * k4a.dll
+ *
+ * \endxmlonly
+ */
+K4ARECORD_EXPORT k4a_result_t k4a_record_set_debug_message_handler(k4a_logging_message_cb_t *message_cb,
+ void *message_cb_context,
+ k4a_log_level_t min_level);
/**
* @}
*/
diff --git a/src/csharp/Examples/Recording/Microsoft.Azure.Kinect.Sensor.Examples.Recording.csproj b/src/csharp/Examples/Recording/Microsoft.Azure.Kinect.Sensor.Examples.Recording.csproj
new file mode 100644
index 000000000..27d7bbb66
--- /dev/null
+++ b/src/csharp/Examples/Recording/Microsoft.Azure.Kinect.Sensor.Examples.Recording.csproj
@@ -0,0 +1,52 @@
+
+
+
+
+ Exe
+ netcoreapp2.1
+ dotnetrecording
+
+ x64;x86
+ false
+ ..\..\AzureKinectSensorSDK.ruleset
+ $(BaseOutputPath)\$(AssemblyName)\
+
+
+
+
+
+
+
+
+
+ stylecop.json
+
+
+
+
+
+ k4a.dll
+ PreserveNewest
+
+
+ k4a.pdb
+ PreserveNewest
+
+
+ k4arecord.dll
+ PreserveNewest
+
+
+ k4arecord.pdb
+ PreserveNewest
+
+
+
+
+ depthengine_2_0.dll
+ PreserveNewest
+
+
+
+
diff --git a/src/csharp/Examples/Recording/Program.cs b/src/csharp/Examples/Recording/Program.cs
new file mode 100644
index 000000000..f7d4898ed
--- /dev/null
+++ b/src/csharp/Examples/Recording/Program.cs
@@ -0,0 +1,102 @@
+using System;
+using System.Linq.Expressions;
+using Microsoft.Azure.Kinect.Sensor;
+using Microsoft.Azure.Kinect.Sensor.Record;
+
+namespace Recording
+{
+ class Program
+ {
+ static void Main(string[] args)
+ {
+ int frame = 0;
+
+ if (args.Length < 1)
+ {
+ Console.WriteLine("Please specify the name of an .mkv output file.");
+ return;
+ }
+
+ string path = args[0];
+
+ try
+ {
+ Console.WriteLine($"Recording from device to \"{path}\".");
+
+ DeviceConfiguration configuration = new DeviceConfiguration()
+ {
+ CameraFPS = FPS.FPS30,
+ ColorFormat = ImageFormat.ColorMJPG,
+ ColorResolution = ColorResolution.R720p,
+ DepthMode = DepthMode.NFOV_2x2Binned,
+ SynchronizedImagesOnly = true
+ };
+ using (Device device = Device.Open())
+ using (Recorder recorder = Recorder.Create(path, device, configuration))
+ {
+
+ device.StartCameras(configuration);
+ device.StartImu();
+
+ recorder.AddImuTrack();
+ recorder.WriteHeader();
+
+ for (frame = 0; frame < 100; frame++)
+ {
+ using (Capture capture = device.GetCapture())
+ {
+ recorder.WriteCapture(capture);
+ Console.WriteLine($"Wrote capture ({capture.Color.DeviceTimestamp})");
+ try
+ {
+ while (true)
+ {
+ // Throws TimeoutException when Imu sample is not available
+ ImuSample sample = device.GetImuSample(TimeSpan.Zero);
+
+ recorder.WriteImuSample(sample);
+ Console.WriteLine($"Wrote imu ({sample.AccelerometerTimestamp})");
+ }
+ }
+ catch (TimeoutException)
+ {
+
+ }
+ }
+ }
+ }
+
+ Console.WriteLine($"Wrote {frame} frames to output.mkv");
+
+ using (Playback playback = Playback.Open(@"output.mkv"))
+ {
+ Console.WriteLine($"Tracks = {playback.TrackCount}");
+ Console.WriteLine($"RecordingLength = {playback.RecordingLength}");
+
+ for (int i = 0; i < playback.TrackCount; i++)
+ {
+ string name = playback.GetTrackName(i);
+ string codecId = playback.GetTrackCodecId(name);
+
+ Console.WriteLine($" Track {i}: {name} ({codecId}) (builtin={playback.GetTrackIsBuiltin(name)})");
+ }
+ Capture capture;
+ while (null != (capture = playback.GetNextCapture()))
+ {
+ Console.WriteLine($"Color timestamp: {capture.Color.DeviceTimestamp} Depth timestamp: {capture.Depth.DeviceTimestamp}");
+ }
+ }
+
+ } catch (AzureKinectException exception)
+ {
+ Console.WriteLine(exception.ToString());
+ Console.WriteLine();
+ Console.WriteLine("Azure Kinect log messages:");
+ foreach (LogMessage m in exception.LogMessages)
+ {
+ Console.WriteLine(m.ToString());
+ }
+ }
+ }
+ }
+}
diff --git a/src/csharp/Examples/WPF/Microsoft.Azure.Kinect.Sensor.Examples.WPFViewer.csproj b/src/csharp/Examples/WPF/Microsoft.Azure.Kinect.Sensor.Examples.WPFViewer.csproj
index c7c7cc0a0..f9723e9d6 100644
--- a/src/csharp/Examples/WPF/Microsoft.Azure.Kinect.Sensor.Examples.WPFViewer.csproj
+++ b/src/csharp/Examples/WPF/Microsoft.Azure.Kinect.Sensor.Examples.WPFViewer.csproj
@@ -159,7 +159,9 @@
k4a.pdb
PreserveNewest
-
+
+
depthengine_2_0.dll
PreserveNewest
diff --git a/src/csharp/Examples/WinForms/Microsoft.Azure.Kinect.Sensor.Examples.WinForms.csproj b/src/csharp/Examples/WinForms/Microsoft.Azure.Kinect.Sensor.Examples.WinForms.csproj
index a8882acf2..f52258d7d 100644
--- a/src/csharp/Examples/WinForms/Microsoft.Azure.Kinect.Sensor.Examples.WinForms.csproj
+++ b/src/csharp/Examples/WinForms/Microsoft.Azure.Kinect.Sensor.Examples.WinForms.csproj
@@ -148,7 +148,9 @@
k4a.pdb
PreserveNewest
-
+
+
depthengine_2_0.dll
PreserveNewest
diff --git a/src/csharp/K4a.sln b/src/csharp/K4a.sln
index a2cb9c758..5c3f2e0e3 100644
--- a/src/csharp/K4a.sln
+++ b/src/csharp/K4a.sln
@@ -40,86 +40,154 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{D9
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Extensions", "Extensions", "{94E07BE5-5E5C-488B-A5CD-0D7D9EBCC725}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Azure.Kinect.Sensor.Examples.Recording", "Examples\Recording\Microsoft.Azure.Kinect.Sensor.Examples.Recording.csproj", "{568BBB67-4EE0-4A0D-AD69-5D10386E2D40}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Azure.Kinect.Sensor.Record", "Record\Microsoft.Azure.Kinect.Sensor.Record.csproj", "{71EAC57F-4023-4B45-8F9E-4A7C05A6BDB3}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Azure.Kinect.Sensor.Record.UnitTests", "Tests\Record.UnitTests\Microsoft.Azure.Kinect.Sensor.Record.UnitTests.csproj", "{4CAEC910-CEC0-41CD-8E47-AF20F5570203}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
+ Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {847B31D5-C253-4766-BF81-032F4670589D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {847B31D5-C253-4766-BF81-032F4670589D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{847B31D5-C253-4766-BF81-032F4670589D}.Debug|x64.ActiveCfg = Debug|Any CPU
{847B31D5-C253-4766-BF81-032F4670589D}.Debug|x64.Build.0 = Debug|Any CPU
{847B31D5-C253-4766-BF81-032F4670589D}.Debug|x86.ActiveCfg = Debug|Any CPU
{847B31D5-C253-4766-BF81-032F4670589D}.Debug|x86.Build.0 = Debug|Any CPU
+ {847B31D5-C253-4766-BF81-032F4670589D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {847B31D5-C253-4766-BF81-032F4670589D}.Release|Any CPU.Build.0 = Release|Any CPU
{847B31D5-C253-4766-BF81-032F4670589D}.Release|x64.ActiveCfg = Release|Any CPU
{847B31D5-C253-4766-BF81-032F4670589D}.Release|x64.Build.0 = Release|Any CPU
{847B31D5-C253-4766-BF81-032F4670589D}.Release|x86.ActiveCfg = Release|Any CPU
{847B31D5-C253-4766-BF81-032F4670589D}.Release|x86.Build.0 = Release|Any CPU
+ {4762DD42-3CF3-4742-9AEA-5D39781FD2A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4762DD42-3CF3-4742-9AEA-5D39781FD2A6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4762DD42-3CF3-4742-9AEA-5D39781FD2A6}.Debug|x64.ActiveCfg = Debug|Any CPU
{4762DD42-3CF3-4742-9AEA-5D39781FD2A6}.Debug|x64.Build.0 = Debug|Any CPU
{4762DD42-3CF3-4742-9AEA-5D39781FD2A6}.Debug|x86.ActiveCfg = Debug|Any CPU
{4762DD42-3CF3-4742-9AEA-5D39781FD2A6}.Debug|x86.Build.0 = Debug|Any CPU
+ {4762DD42-3CF3-4742-9AEA-5D39781FD2A6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4762DD42-3CF3-4742-9AEA-5D39781FD2A6}.Release|Any CPU.Build.0 = Release|Any CPU
{4762DD42-3CF3-4742-9AEA-5D39781FD2A6}.Release|x64.ActiveCfg = Release|Any CPU
{4762DD42-3CF3-4742-9AEA-5D39781FD2A6}.Release|x64.Build.0 = Release|Any CPU
{4762DD42-3CF3-4742-9AEA-5D39781FD2A6}.Release|x86.ActiveCfg = Release|Any CPU
{4762DD42-3CF3-4742-9AEA-5D39781FD2A6}.Release|x86.Build.0 = Release|Any CPU
+ {618E28C5-0624-463F-9ADB-040BA5DAED68}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {618E28C5-0624-463F-9ADB-040BA5DAED68}.Debug|Any CPU.Build.0 = Debug|Any CPU
{618E28C5-0624-463F-9ADB-040BA5DAED68}.Debug|x64.ActiveCfg = Debug|Any CPU
{618E28C5-0624-463F-9ADB-040BA5DAED68}.Debug|x64.Build.0 = Debug|Any CPU
{618E28C5-0624-463F-9ADB-040BA5DAED68}.Debug|x86.ActiveCfg = Debug|Any CPU
{618E28C5-0624-463F-9ADB-040BA5DAED68}.Debug|x86.Build.0 = Debug|Any CPU
+ {618E28C5-0624-463F-9ADB-040BA5DAED68}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {618E28C5-0624-463F-9ADB-040BA5DAED68}.Release|Any CPU.Build.0 = Release|Any CPU
{618E28C5-0624-463F-9ADB-040BA5DAED68}.Release|x64.ActiveCfg = Release|Any CPU
{618E28C5-0624-463F-9ADB-040BA5DAED68}.Release|x64.Build.0 = Release|Any CPU
{618E28C5-0624-463F-9ADB-040BA5DAED68}.Release|x86.ActiveCfg = Release|Any CPU
{618E28C5-0624-463F-9ADB-040BA5DAED68}.Release|x86.Build.0 = Release|Any CPU
+ {CCD99E9D-1EE2-41F5-AD3F-4110A466A9A4}.Debug|Any CPU.ActiveCfg = Debug|x86
{CCD99E9D-1EE2-41F5-AD3F-4110A466A9A4}.Debug|x64.ActiveCfg = Debug|x64
{CCD99E9D-1EE2-41F5-AD3F-4110A466A9A4}.Debug|x64.Build.0 = Debug|x64
{CCD99E9D-1EE2-41F5-AD3F-4110A466A9A4}.Debug|x86.ActiveCfg = Debug|x86
{CCD99E9D-1EE2-41F5-AD3F-4110A466A9A4}.Debug|x86.Build.0 = Debug|x86
+ {CCD99E9D-1EE2-41F5-AD3F-4110A466A9A4}.Release|Any CPU.ActiveCfg = Release|x86
{CCD99E9D-1EE2-41F5-AD3F-4110A466A9A4}.Release|x64.ActiveCfg = Release|x64
{CCD99E9D-1EE2-41F5-AD3F-4110A466A9A4}.Release|x64.Build.0 = Release|x64
{CCD99E9D-1EE2-41F5-AD3F-4110A466A9A4}.Release|x86.ActiveCfg = Release|x86
{CCD99E9D-1EE2-41F5-AD3F-4110A466A9A4}.Release|x86.Build.0 = Release|x86
+ {8A14FB66-07CD-4E4C-A533-89DE0AFF4FCB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8A14FB66-07CD-4E4C-A533-89DE0AFF4FCB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8A14FB66-07CD-4E4C-A533-89DE0AFF4FCB}.Debug|x64.ActiveCfg = Debug|x64
{8A14FB66-07CD-4E4C-A533-89DE0AFF4FCB}.Debug|x64.Build.0 = Debug|x64
{8A14FB66-07CD-4E4C-A533-89DE0AFF4FCB}.Debug|x86.ActiveCfg = Debug|x86
{8A14FB66-07CD-4E4C-A533-89DE0AFF4FCB}.Debug|x86.Build.0 = Debug|x86
+ {8A14FB66-07CD-4E4C-A533-89DE0AFF4FCB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8A14FB66-07CD-4E4C-A533-89DE0AFF4FCB}.Release|Any CPU.Build.0 = Release|Any CPU
{8A14FB66-07CD-4E4C-A533-89DE0AFF4FCB}.Release|x64.ActiveCfg = Release|x64
{8A14FB66-07CD-4E4C-A533-89DE0AFF4FCB}.Release|x64.Build.0 = Release|x64
{8A14FB66-07CD-4E4C-A533-89DE0AFF4FCB}.Release|x86.ActiveCfg = Release|x86
{8A14FB66-07CD-4E4C-A533-89DE0AFF4FCB}.Release|x86.Build.0 = Release|x86
+ {6D4EC05A-3A81-4B92-8881-96F499F5986B}.Debug|Any CPU.ActiveCfg = Debug|x86
{6D4EC05A-3A81-4B92-8881-96F499F5986B}.Debug|x64.ActiveCfg = Debug|x64
{6D4EC05A-3A81-4B92-8881-96F499F5986B}.Debug|x64.Build.0 = Debug|x64
{6D4EC05A-3A81-4B92-8881-96F499F5986B}.Debug|x86.ActiveCfg = Debug|x86
{6D4EC05A-3A81-4B92-8881-96F499F5986B}.Debug|x86.Build.0 = Debug|x86
+ {6D4EC05A-3A81-4B92-8881-96F499F5986B}.Release|Any CPU.ActiveCfg = Release|x86
{6D4EC05A-3A81-4B92-8881-96F499F5986B}.Release|x64.ActiveCfg = Release|x64
{6D4EC05A-3A81-4B92-8881-96F499F5986B}.Release|x64.Build.0 = Release|x64
{6D4EC05A-3A81-4B92-8881-96F499F5986B}.Release|x86.ActiveCfg = Release|x86
{6D4EC05A-3A81-4B92-8881-96F499F5986B}.Release|x86.Build.0 = Release|x86
+ {41510BD0-7F25-470B-A1DC-12E1DB1AB3B7}.Debug|Any CPU.ActiveCfg = Debug|x86
{41510BD0-7F25-470B-A1DC-12E1DB1AB3B7}.Debug|x64.ActiveCfg = Debug|x64
{41510BD0-7F25-470B-A1DC-12E1DB1AB3B7}.Debug|x64.Build.0 = Debug|x64
{41510BD0-7F25-470B-A1DC-12E1DB1AB3B7}.Debug|x86.ActiveCfg = Debug|x86
{41510BD0-7F25-470B-A1DC-12E1DB1AB3B7}.Debug|x86.Build.0 = Debug|x86
+ {41510BD0-7F25-470B-A1DC-12E1DB1AB3B7}.Release|Any CPU.ActiveCfg = Release|x86
{41510BD0-7F25-470B-A1DC-12E1DB1AB3B7}.Release|x64.ActiveCfg = Release|x64
{41510BD0-7F25-470B-A1DC-12E1DB1AB3B7}.Release|x64.Build.0 = Release|x64
{41510BD0-7F25-470B-A1DC-12E1DB1AB3B7}.Release|x86.ActiveCfg = Release|x86
{41510BD0-7F25-470B-A1DC-12E1DB1AB3B7}.Release|x86.Build.0 = Release|x86
+ {FCD1E629-1E96-4BDD-A247-35B50F31137A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FCD1E629-1E96-4BDD-A247-35B50F31137A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FCD1E629-1E96-4BDD-A247-35B50F31137A}.Debug|x64.ActiveCfg = Debug|Any CPU
{FCD1E629-1E96-4BDD-A247-35B50F31137A}.Debug|x64.Build.0 = Debug|Any CPU
{FCD1E629-1E96-4BDD-A247-35B50F31137A}.Debug|x86.ActiveCfg = Debug|Any CPU
{FCD1E629-1E96-4BDD-A247-35B50F31137A}.Debug|x86.Build.0 = Debug|Any CPU
+ {FCD1E629-1E96-4BDD-A247-35B50F31137A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FCD1E629-1E96-4BDD-A247-35B50F31137A}.Release|Any CPU.Build.0 = Release|Any CPU
{FCD1E629-1E96-4BDD-A247-35B50F31137A}.Release|x64.ActiveCfg = Release|Any CPU
{FCD1E629-1E96-4BDD-A247-35B50F31137A}.Release|x64.Build.0 = Release|Any CPU
{FCD1E629-1E96-4BDD-A247-35B50F31137A}.Release|x86.ActiveCfg = Release|Any CPU
{FCD1E629-1E96-4BDD-A247-35B50F31137A}.Release|x86.Build.0 = Release|Any CPU
+ {E1B3CC41-BC1C-47B7-A6A6-AA50E6994C05}.Debug|Any CPU.ActiveCfg = Debug|x86
{E1B3CC41-BC1C-47B7-A6A6-AA50E6994C05}.Debug|x64.ActiveCfg = Debug|x64
{E1B3CC41-BC1C-47B7-A6A6-AA50E6994C05}.Debug|x64.Build.0 = Debug|x64
{E1B3CC41-BC1C-47B7-A6A6-AA50E6994C05}.Debug|x86.ActiveCfg = Debug|x86
{E1B3CC41-BC1C-47B7-A6A6-AA50E6994C05}.Debug|x86.Build.0 = Debug|x86
+ {E1B3CC41-BC1C-47B7-A6A6-AA50E6994C05}.Release|Any CPU.ActiveCfg = Release|x86
{E1B3CC41-BC1C-47B7-A6A6-AA50E6994C05}.Release|x64.ActiveCfg = Release|x64
{E1B3CC41-BC1C-47B7-A6A6-AA50E6994C05}.Release|x64.Build.0 = Release|x64
{E1B3CC41-BC1C-47B7-A6A6-AA50E6994C05}.Release|x86.ActiveCfg = Release|x86
{E1B3CC41-BC1C-47B7-A6A6-AA50E6994C05}.Release|x86.Build.0 = Release|x86
+ {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Debug|Any CPU.ActiveCfg = Debug|x86
+ {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Debug|x64.ActiveCfg = Debug|x64
+ {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Debug|x64.Build.0 = Debug|x64
+ {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Debug|x86.ActiveCfg = Debug|x86
+ {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Debug|x86.Build.0 = Debug|x86
+ {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Release|Any CPU.ActiveCfg = Release|x86
+ {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Release|x64.ActiveCfg = Release|x64
+ {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Release|x64.Build.0 = Release|x64
+ {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Release|x86.ActiveCfg = Release|x86
+ {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Release|x86.Build.0 = Release|x86
+ {71EAC57F-4023-4B45-8F9E-4A7C05A6BDB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {71EAC57F-4023-4B45-8F9E-4A7C05A6BDB3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {71EAC57F-4023-4B45-8F9E-4A7C05A6BDB3}.Debug|x64.ActiveCfg = Debug|x64
+ {71EAC57F-4023-4B45-8F9E-4A7C05A6BDB3}.Debug|x64.Build.0 = Debug|x64
+ {71EAC57F-4023-4B45-8F9E-4A7C05A6BDB3}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {71EAC57F-4023-4B45-8F9E-4A7C05A6BDB3}.Debug|x86.Build.0 = Debug|Any CPU
+ {71EAC57F-4023-4B45-8F9E-4A7C05A6BDB3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {71EAC57F-4023-4B45-8F9E-4A7C05A6BDB3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {71EAC57F-4023-4B45-8F9E-4A7C05A6BDB3}.Release|x64.ActiveCfg = Release|Any CPU
+ {71EAC57F-4023-4B45-8F9E-4A7C05A6BDB3}.Release|x64.Build.0 = Release|Any CPU
+ {71EAC57F-4023-4B45-8F9E-4A7C05A6BDB3}.Release|x86.ActiveCfg = Release|Any CPU
+ {71EAC57F-4023-4B45-8F9E-4A7C05A6BDB3}.Release|x86.Build.0 = Release|Any CPU
+ {4CAEC910-CEC0-41CD-8E47-AF20F5570203}.Debug|Any CPU.ActiveCfg = Debug|x86
+ {4CAEC910-CEC0-41CD-8E47-AF20F5570203}.Debug|x64.ActiveCfg = Debug|x64
+ {4CAEC910-CEC0-41CD-8E47-AF20F5570203}.Debug|x64.Build.0 = Debug|x64
+ {4CAEC910-CEC0-41CD-8E47-AF20F5570203}.Debug|x86.ActiveCfg = Debug|x86
+ {4CAEC910-CEC0-41CD-8E47-AF20F5570203}.Debug|x86.Build.0 = Debug|x86
+ {4CAEC910-CEC0-41CD-8E47-AF20F5570203}.Release|Any CPU.ActiveCfg = Release|x86
+ {4CAEC910-CEC0-41CD-8E47-AF20F5570203}.Release|x64.ActiveCfg = Release|x64
+ {4CAEC910-CEC0-41CD-8E47-AF20F5570203}.Release|x64.Build.0 = Release|x64
+ {4CAEC910-CEC0-41CD-8E47-AF20F5570203}.Release|x86.ActiveCfg = Release|x86
+ {4CAEC910-CEC0-41CD-8E47-AF20F5570203}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -134,6 +202,8 @@ Global
{5C3A9F92-56B4-4A7B-86EC-BACBE07C5AAE} = {21E41070-E020-49B0-9976-54F92B2251DD}
{FCD1E629-1E96-4BDD-A247-35B50F31137A} = {5C3A9F92-56B4-4A7B-86EC-BACBE07C5AAE}
{E1B3CC41-BC1C-47B7-A6A6-AA50E6994C05} = {5C3A9F92-56B4-4A7B-86EC-BACBE07C5AAE}
+ {568BBB67-4EE0-4A0D-AD69-5D10386E2D40} = {D946946D-56B5-4F64-B4FC-5C79F15295C4}
+ {4CAEC910-CEC0-41CD-8E47-AF20F5570203} = {21E41070-E020-49B0-9976-54F92B2251DD}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {9BC05C93-252F-4030-ACA6-41B4B54F9C86}
diff --git a/src/csharp/Record/DataBlock.cs b/src/csharp/Record/DataBlock.cs
new file mode 100644
index 000000000..a9c7e2cda
--- /dev/null
+++ b/src/csharp/Record/DataBlock.cs
@@ -0,0 +1,115 @@
+//------------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+//
+//------------------------------------------------------------------------------
+using System;
+using System.Buffers;
+using System.Runtime.InteropServices;
+
+namespace Microsoft.Azure.Kinect.Sensor.Record
+{
+ ///
+ /// Represents a block of data from a custom recording track.
+ ///
+ public class DataBlock : IDisposable, IMemoryOwner
+ {
+ // The native handle for this data block.
+ private readonly NativeMethods.k4a_playback_data_block_t handle;
+
+ // To detect redundant calls to Dispose
+ private bool disposedValue = false;
+
+ private byte[] buffer = null;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Native handle to the data block.
+ internal DataBlock(NativeMethods.k4a_playback_data_block_t handle)
+ {
+ this.handle = handle;
+ }
+
+ ///
+ /// Gets the memory with the custom data.
+ ///
+ public Memory Memory
+ {
+ get
+ {
+ lock (this)
+ {
+ if (this.disposedValue)
+ {
+ throw new ObjectDisposedException(nameof(DataBlock));
+ }
+
+ if (this.buffer == null)
+ {
+ ulong bufferSize = NativeMethods.k4a_playback_data_block_get_buffer_size(this.handle);
+
+ this.buffer = new byte[bufferSize];
+
+ IntPtr bufferPtr = NativeMethods.k4a_playback_data_block_get_buffer(this.handle);
+
+ if (bufferPtr != IntPtr.Zero)
+ {
+ Marshal.Copy(bufferPtr, this.buffer, 0, checked((int)bufferSize));
+ }
+ else
+ {
+ this.buffer = null;
+ }
+ }
+
+ return this.buffer;
+ }
+ }
+ }
+
+ ///
+ /// Gets the device timestamp associated with the data.
+ ///
+ public TimeSpan DeviceTimestamp
+ {
+ get
+ {
+ lock (this)
+ {
+ if (this.disposedValue)
+ {
+ throw new ObjectDisposedException(nameof(DataBlock));
+ }
+
+ ulong timeStamp = NativeMethods.k4a_playback_data_block_get_device_timestamp_usec(this.handle);
+
+ return TimeSpan.FromTicks(checked((long)timeStamp) * 10);
+ }
+ }
+ }
+
+ ///
+ public void Dispose()
+ {
+ this.Dispose(true);
+
+ GC.SuppressFinalize(this);
+ }
+
+ ///
+ /// Handle the disposing of the object.
+ ///
+ /// true when called by Dispose(), false when called by the finalizer.
+ protected virtual void Dispose(bool disposing)
+ {
+ lock (this)
+ {
+ this.handle.Close();
+
+ this.disposedValue = true;
+ }
+ }
+ }
+}
diff --git a/src/csharp/Record/Exceptions/AzureKinectAddAttachmentException.cs b/src/csharp/Record/Exceptions/AzureKinectAddAttachmentException.cs
new file mode 100644
index 000000000..aacf0d6a6
--- /dev/null
+++ b/src/csharp/Record/Exceptions/AzureKinectAddAttachmentException.cs
@@ -0,0 +1,101 @@
+//------------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+//
+//------------------------------------------------------------------------------
+using System;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions
+{
+ ///
+ /// Represents errors that occur when adding an attachment to a recording.
+ ///
+ [Serializable]
+ public class AzureKinectAddAttachmentException : AzureKinectRecordException
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AzureKinectAddAttachmentException()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ public AzureKinectAddAttachmentException(string message)
+ : base(message)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message and a reference to the inner exception that is the
+ /// cause of this exception.
+ ///
+ ///
+ /// The error message that explains the reason for the exception.
+ ///
+ ///
+ /// The exception that is the cause of the current exception, or a null reference
+ /// (Nothing in Visual Basic) if no inner exception is specified.
+ ///
+ public AzureKinectAddAttachmentException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with serialized data.
+ ///
+ ///
+ /// The that holds the serialized object data about the
+ /// exception being thrown.
+ ///
+ /// The System.Runtime.Serialization.StreamingContext that
+ /// contains contextual information about the source or destination.
+ ///
+ protected AzureKinectAddAttachmentException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ ///
+ /// The log messages that happened during the function call that generated this error.
+ ///
+ protected AzureKinectAddAttachmentException(string message, ICollection logMessages)
+ : base(message, logMessages)
+ {
+ }
+
+ ///
+ /// Throws an if the result of the function
+ /// is not a success.
+ ///
+ /// The native function to call.
+ /// The type of result to expect from the function call.
+ internal static void ThrowIfNotSuccess(Func function)
+ where T : System.Enum
+ {
+ using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider))
+ {
+ T result = function();
+ if (!AzureKinectRecordException.IsSuccess(result))
+ {
+ throw new AzureKinectAddAttachmentException($"result = {result}", tracer.LogMessages);
+ }
+ }
+ }
+ }
+}
diff --git a/src/csharp/Record/Exceptions/AzureKinectAddCustomSubtitleTrackException.cs b/src/csharp/Record/Exceptions/AzureKinectAddCustomSubtitleTrackException.cs
new file mode 100644
index 000000000..12527430e
--- /dev/null
+++ b/src/csharp/Record/Exceptions/AzureKinectAddCustomSubtitleTrackException.cs
@@ -0,0 +1,101 @@
+//------------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+//
+//------------------------------------------------------------------------------
+using System;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions
+{
+ ///
+ /// Represents errors that occur when adding a custom subtitle track.
+ ///
+ [Serializable]
+ public class AzureKinectAddCustomSubtitleTrackException : AzureKinectRecordException
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AzureKinectAddCustomSubtitleTrackException()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ public AzureKinectAddCustomSubtitleTrackException(string message)
+ : base(message)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message and a reference to the inner exception that is the
+ /// cause of this exception.
+ ///
+ ///
+ /// The error message that explains the reason for the exception.
+ ///
+ ///
+ /// The exception that is the cause of the current exception, or a null reference
+ /// (Nothing in Visual Basic) if no inner exception is specified.
+ ///
+ public AzureKinectAddCustomSubtitleTrackException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with serialized data.
+ ///
+ ///
+ /// The that holds the serialized object data about the
+ /// exception being thrown.
+ ///
+ /// The System.Runtime.Serialization.StreamingContext that
+ /// contains contextual information about the source or destination.
+ ///
+ protected AzureKinectAddCustomSubtitleTrackException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ ///
+ /// The log messages that happened during the function call that generated this error.
+ ///
+ protected AzureKinectAddCustomSubtitleTrackException(string message, ICollection logMessages)
+ : base(message, logMessages)
+ {
+ }
+
+ ///
+ /// Throws an if the result of the function
+ /// is not a success.
+ ///
+ /// The native function to call.
+ /// The type of result to expect from the function call.
+ internal static void ThrowIfNotSuccess(Func function)
+ where T : System.Enum
+ {
+ using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider))
+ {
+ T result = function();
+ if (!AzureKinectRecordException.IsSuccess(result))
+ {
+ throw new AzureKinectAddCustomSubtitleTrackException($"result = {result}", tracer.LogMessages);
+ }
+ }
+ }
+ }
+}
diff --git a/src/csharp/Record/Exceptions/AzureKinectAddCustomVideoTrackException.cs b/src/csharp/Record/Exceptions/AzureKinectAddCustomVideoTrackException.cs
new file mode 100644
index 000000000..aa59301cd
--- /dev/null
+++ b/src/csharp/Record/Exceptions/AzureKinectAddCustomVideoTrackException.cs
@@ -0,0 +1,101 @@
+//------------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+//
+//------------------------------------------------------------------------------
+using System;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions
+{
+ ///
+ /// Represents errors that occur when adding a custom video track.
+ ///
+ [Serializable]
+ public class AzureKinectAddCustomVideoTrackException : AzureKinectRecordException
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AzureKinectAddCustomVideoTrackException()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ public AzureKinectAddCustomVideoTrackException(string message)
+ : base(message)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message and a reference to the inner exception that is the
+ /// cause of this exception.
+ ///
+ ///
+ /// The error message that explains the reason for the exception.
+ ///
+ ///
+ /// The exception that is the cause of the current exception, or a null reference
+ /// (Nothing in Visual Basic) if no inner exception is specified.
+ ///
+ public AzureKinectAddCustomVideoTrackException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with serialized data.
+ ///
+ ///
+ /// The that holds the serialized object data about the
+ /// exception being thrown.
+ ///
+ /// The System.Runtime.Serialization.StreamingContext that
+ /// contains contextual information about the source or destination.
+ ///
+ protected AzureKinectAddCustomVideoTrackException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ ///
+ /// The log messages that happened during the function call that generated this error.
+ ///
+ protected AzureKinectAddCustomVideoTrackException(string message, ICollection logMessages)
+ : base(message, logMessages)
+ {
+ }
+
+ ///
+ /// Throws an if the result of the function
+ /// is not a success.
+ ///
+ /// The native function to call.
+ /// The type of result to expect from the function call.
+ internal static void ThrowIfNotSuccess(Func function)
+ where T : System.Enum
+ {
+ using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider))
+ {
+ T result = function();
+ if (!AzureKinectRecordException.IsSuccess(result))
+ {
+ throw new AzureKinectAddCustomVideoTrackException($"result = {result}", tracer.LogMessages);
+ }
+ }
+ }
+ }
+}
diff --git a/src/csharp/Record/Exceptions/AzureKinectAddImuTrackException.cs b/src/csharp/Record/Exceptions/AzureKinectAddImuTrackException.cs
new file mode 100644
index 000000000..3fadd2128
--- /dev/null
+++ b/src/csharp/Record/Exceptions/AzureKinectAddImuTrackException.cs
@@ -0,0 +1,101 @@
+//------------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+//
+//------------------------------------------------------------------------------
+using System;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions
+{
+ ///
+ /// Represents errors that occur when adding an IMU track to a recording.
+ ///
+ [Serializable]
+ public class AzureKinectAddImuTrackException : AzureKinectRecordException
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AzureKinectAddImuTrackException()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ public AzureKinectAddImuTrackException(string message)
+ : base(message)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message and a reference to the inner exception that is the
+ /// cause of this exception.
+ ///
+ ///
+ /// The error message that explains the reason for the exception.
+ ///
+ ///
+ /// The exception that is the cause of the current exception, or a null reference
+ /// (Nothing in Visual Basic) if no inner exception is specified.
+ ///
+ public AzureKinectAddImuTrackException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with serialized data.
+ ///
+ ///
+ /// The that holds the serialized object data about the
+ /// exception being thrown.
+ ///
+ /// The System.Runtime.Serialization.StreamingContext that
+ /// contains contextual information about the source or destination.
+ ///
+ protected AzureKinectAddImuTrackException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ ///
+ /// The log messages that happened during the function call that generated this error.
+ ///
+ protected AzureKinectAddImuTrackException(string message, ICollection logMessages)
+ : base(message, logMessages)
+ {
+ }
+
+ ///
+ /// Throws an if the result of the function
+ /// is not a success.
+ ///
+ /// The native function to call.
+ /// The type of result to expect from the function call.
+ internal static void ThrowIfNotSuccess(Func function)
+ where T : System.Enum
+ {
+ using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider))
+ {
+ T result = function();
+ if (!AzureKinectRecordException.IsSuccess(result))
+ {
+ throw new AzureKinectAddImuTrackException($"result = {result}", tracer.LogMessages);
+ }
+ }
+ }
+ }
+}
diff --git a/src/csharp/Record/Exceptions/AzureKinectAddTagException.cs b/src/csharp/Record/Exceptions/AzureKinectAddTagException.cs
new file mode 100644
index 000000000..070d9ca06
--- /dev/null
+++ b/src/csharp/Record/Exceptions/AzureKinectAddTagException.cs
@@ -0,0 +1,117 @@
+//------------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+//
+//------------------------------------------------------------------------------
+using System;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions
+{
+ ///
+ /// Represents errors that occur when adding a tag to a recording.
+ ///
+ [Serializable]
+ public class AzureKinectAddTagException : AzureKinectRecordException
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AzureKinectAddTagException()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ public AzureKinectAddTagException(string message)
+ : base(message)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message and a reference to the inner exception that is the
+ /// cause of this exception.
+ ///
+ ///
+ /// The error message that explains the reason for the exception.
+ ///
+ ///
+ /// The exception that is the cause of the current exception, or a null reference
+ /// (Nothing in Visual Basic) if no inner exception is specified.
+ ///
+ public AzureKinectAddTagException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with serialized data.
+ ///
+ ///
+ /// The that holds the serialized object data about the
+ /// exception being thrown.
+ ///
+ /// The System.Runtime.Serialization.StreamingContext that
+ /// contains contextual information about the source or destination.
+ ///
+ protected AzureKinectAddTagException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ ///
+ /// The log messages that happened during the function call that generated this error.
+ ///
+ protected AzureKinectAddTagException(string message, ICollection logMessages)
+ : base(message, logMessages)
+ {
+ }
+
+ ///
+ /// Throws an if the result of the function
+ /// is not a success.
+ ///
+ /// The native function to call.
+ /// The type of result to expect from the function call.
+ internal static void ThrowIfNotSuccess(Func function)
+ where T : System.Enum
+ {
+ using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider))
+ {
+ T result = function();
+ if (!AzureKinectRecordException.IsSuccess(result))
+ {
+ throw new AzureKinectAddTagException($"result = {result}", tracer.LogMessages);
+ }
+ }
+ }
+
+ ///
+ /// Throws an if the result of the function
+ /// is not a success.
+ ///
+ /// The tracer is that is capturing logging messages.
+ /// The result native function to call.
+ /// The type of result to expect from the function call.
+ internal static void ThrowIfNotSuccess(LoggingTracer tracer, T result)
+ where T : System.Enum
+ {
+ if (!AzureKinectRecordException.IsSuccess(result))
+ {
+ throw new AzureKinectAddTagException($"result = {result}", tracer.LogMessages);
+ }
+ }
+ }
+}
diff --git a/src/csharp/Record/Exceptions/AzureKinectCreateRecordingException.cs b/src/csharp/Record/Exceptions/AzureKinectCreateRecordingException.cs
new file mode 100644
index 000000000..b6ef82e17
--- /dev/null
+++ b/src/csharp/Record/Exceptions/AzureKinectCreateRecordingException.cs
@@ -0,0 +1,119 @@
+//------------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+//
+//------------------------------------------------------------------------------
+using System;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions
+{
+ ///
+ /// Represents errors that occur when creating an Azure Kinect sensor recording.
+ ///
+ [Serializable]
+ public class AzureKinectCreateRecordingException : AzureKinectRecordException
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AzureKinectCreateRecordingException()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ public AzureKinectCreateRecordingException(string message)
+ : base(message)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message and a reference to the inner exception that is the
+ /// cause of this exception.
+ ///
+ ///
+ /// The error message that explains the reason for the exception.
+ ///
+ ///
+ /// The exception that is the cause of the current exception, or a null reference
+ /// (Nothing in Visual Basic) if no inner exception is specified.
+ ///
+ public AzureKinectCreateRecordingException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with serialized data.
+ ///
+ ///
+ /// The that holds the serialized object data about the
+ /// exception being thrown.
+ ///
+ /// The System.Runtime.Serialization.StreamingContext that
+ /// contains contextual information about the source or destination.
+ ///
+ protected AzureKinectCreateRecordingException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ ///
+ /// The log messages that happened during the function call that generated this error.
+ ///
+ protected AzureKinectCreateRecordingException(string message, ICollection logMessages)
+ : base(message, logMessages)
+ {
+ }
+
+ ///
+ /// Throws an if the result of the function
+ /// is not a success.
+ ///
+ /// File name of the create.
+ /// The native function to call.
+ /// The type of result to expect from the function call.
+ internal static void ThrowIfNotSuccess(string fileName, Func function)
+ where T : System.Enum
+ {
+ using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider))
+ {
+ T result = function();
+ if (!AzureKinectRecordException.IsSuccess(result))
+ {
+ throw new AzureKinectCreateRecordingException($"fileName = \"{fileName}\"\r\nresult = {result}", tracer.LogMessages);
+ }
+ }
+ }
+
+ ///
+ /// Throws an if the result of the function
+ /// is not a success.
+ ///
+ /// File name of the create.
+ /// The tracer is that is capturing logging messages.
+ /// The result native function to call.
+ /// The type of result to expect from the function call.
+ internal static void ThrowIfNotSuccess(string fileName, LoggingTracer tracer, T result)
+ where T : System.Enum
+ {
+ if (!AzureKinectRecordException.IsSuccess(result))
+ {
+ throw new AzureKinectCreateRecordingException($"fileName = \"{fileName}\"\r\nresult = {result}", tracer.LogMessages);
+ }
+ }
+ }
+}
diff --git a/src/csharp/Record/Exceptions/AzureKinectFlushException.cs b/src/csharp/Record/Exceptions/AzureKinectFlushException.cs
new file mode 100644
index 000000000..e23177971
--- /dev/null
+++ b/src/csharp/Record/Exceptions/AzureKinectFlushException.cs
@@ -0,0 +1,101 @@
+//------------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+//
+//------------------------------------------------------------------------------
+using System;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions
+{
+ ///
+ /// Represents errors that occur when an error occurs during flushing.
+ ///
+ [Serializable]
+ public class AzureKinectFlushException : AzureKinectRecordException
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AzureKinectFlushException()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ public AzureKinectFlushException(string message)
+ : base(message)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message and a reference to the inner exception that is the
+ /// cause of this exception.
+ ///
+ ///
+ /// The error message that explains the reason for the exception.
+ ///
+ ///
+ /// The exception that is the cause of the current exception, or a null reference
+ /// (Nothing in Visual Basic) if no inner exception is specified.
+ ///
+ public AzureKinectFlushException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with serialized data.
+ ///
+ ///
+ /// The that holds the serialized object data about the
+ /// exception being thrown.
+ ///
+ /// The System.Runtime.Serialization.StreamingContext that
+ /// contains contextual information about the source or destination.
+ ///
+ protected AzureKinectFlushException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ ///
+ /// The log messages that happened during the function call that generated this error.
+ ///
+ protected AzureKinectFlushException(string message, ICollection logMessages)
+ : base(message, logMessages)
+ {
+ }
+
+ ///
+ /// Throws an if the result of the function
+ /// is not a success.
+ ///
+ /// The native function to call.
+ /// The type of result to expect from the function call.
+ internal static void ThrowIfNotSuccess(Func function)
+ where T : System.Enum
+ {
+ using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider))
+ {
+ T result = function();
+ if (!AzureKinectRecordException.IsSuccess(result))
+ {
+ throw new AzureKinectFlushException($"result = {result}", tracer.LogMessages);
+ }
+ }
+ }
+ }
+}
diff --git a/src/csharp/Record/Exceptions/AzureKinectGetCalibrationException.cs b/src/csharp/Record/Exceptions/AzureKinectGetCalibrationException.cs
new file mode 100644
index 000000000..8050101ed
--- /dev/null
+++ b/src/csharp/Record/Exceptions/AzureKinectGetCalibrationException.cs
@@ -0,0 +1,101 @@
+//------------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+//
+//------------------------------------------------------------------------------
+using System;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions
+{
+ ///
+ /// Represents errors that occur when getting calibration from a recording.
+ ///
+ [Serializable]
+ public class AzureKinectGetCalibrationException : AzureKinectRecordException
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AzureKinectGetCalibrationException()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ public AzureKinectGetCalibrationException(string message)
+ : base(message)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message and a reference to the inner exception that is the
+ /// cause of this exception.
+ ///
+ ///
+ /// The error message that explains the reason for the exception.
+ ///
+ ///
+ /// The exception that is the cause of the current exception, or a null reference
+ /// (Nothing in Visual Basic) if no inner exception is specified.
+ ///
+ public AzureKinectGetCalibrationException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with serialized data.
+ ///
+ ///
+ /// The that holds the serialized object data about the
+ /// exception being thrown.
+ ///
+ /// The System.Runtime.Serialization.StreamingContext that
+ /// contains contextual information about the source or destination.
+ ///
+ protected AzureKinectGetCalibrationException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ ///
+ /// The log messages that happened during the function call that generated this error.
+ ///
+ protected AzureKinectGetCalibrationException(string message, ICollection logMessages)
+ : base(message, logMessages)
+ {
+ }
+
+ ///
+ /// Throws an if the result of the function
+ /// is not a success.
+ ///
+ /// The native function to call.
+ /// The type of result to expect from the function call.
+ internal static void ThrowIfNotSuccess(Func function)
+ where T : System.Enum
+ {
+ using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider))
+ {
+ T result = function();
+ if (!AzureKinectRecordException.IsSuccess(result))
+ {
+ throw new AzureKinectGetCalibrationException($"result = {result}", tracer.LogMessages);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/csharp/Record/Exceptions/AzureKinectGetCaptureException.cs b/src/csharp/Record/Exceptions/AzureKinectGetCaptureException.cs
new file mode 100644
index 000000000..8960681fb
--- /dev/null
+++ b/src/csharp/Record/Exceptions/AzureKinectGetCaptureException.cs
@@ -0,0 +1,101 @@
+//------------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+//
+//------------------------------------------------------------------------------
+using System;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions
+{
+ ///
+ /// Represents errors that occur when getting the next or previous capture.
+ ///
+ [Serializable]
+ public class AzureKinectGetCaptureException : AzureKinectRecordException
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AzureKinectGetCaptureException()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ public AzureKinectGetCaptureException(string message)
+ : base(message)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message and a reference to the inner exception that is the
+ /// cause of this exception.
+ ///
+ ///
+ /// The error message that explains the reason for the exception.
+ ///
+ ///
+ /// The exception that is the cause of the current exception, or a null reference
+ /// (Nothing in Visual Basic) if no inner exception is specified.
+ ///
+ public AzureKinectGetCaptureException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with serialized data.
+ ///
+ ///
+ /// The that holds the serialized object data about the
+ /// exception being thrown.
+ ///
+ /// The System.Runtime.Serialization.StreamingContext that
+ /// contains contextual information about the source or destination.
+ ///
+ protected AzureKinectGetCaptureException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ ///
+ /// The log messages that happened during the function call that generated this error.
+ ///
+ protected AzureKinectGetCaptureException(string message, ICollection logMessages)
+ : base(message, logMessages)
+ {
+ }
+
+ ///
+ /// Throws an if the result of the function
+ /// is not a success.
+ ///
+ /// The native function to call.
+ /// The type of result to expect from the function call.
+ internal static void ThrowIfNotSuccess(Func function)
+ where T : System.Enum
+ {
+ using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider))
+ {
+ T result = function();
+ if (!AzureKinectRecordException.IsSuccess(result))
+ {
+ throw new AzureKinectGetCaptureException($"result = {result}", tracer.LogMessages);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/csharp/Record/Exceptions/AzureKinectGetDataBlockException.cs b/src/csharp/Record/Exceptions/AzureKinectGetDataBlockException.cs
new file mode 100644
index 000000000..e06bfccab
--- /dev/null
+++ b/src/csharp/Record/Exceptions/AzureKinectGetDataBlockException.cs
@@ -0,0 +1,101 @@
+//------------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+//
+//------------------------------------------------------------------------------
+using System;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions
+{
+ ///
+ /// Represents errors that occur when getting a data block.
+ ///
+ [Serializable]
+ public class AzureKinectGetDataBlockException : AzureKinectRecordException
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AzureKinectGetDataBlockException()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ public AzureKinectGetDataBlockException(string message)
+ : base(message)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message and a reference to the inner exception that is the
+ /// cause of this exception.
+ ///
+ ///
+ /// The error message that explains the reason for the exception.
+ ///
+ ///
+ /// The exception that is the cause of the current exception, or a null reference
+ /// (Nothing in Visual Basic) if no inner exception is specified.
+ ///
+ public AzureKinectGetDataBlockException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with serialized data.
+ ///
+ ///
+ /// The that holds the serialized object data about the
+ /// exception being thrown.
+ ///
+ /// The System.Runtime.Serialization.StreamingContext that
+ /// contains contextual information about the source or destination.
+ ///
+ protected AzureKinectGetDataBlockException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ ///
+ /// The log messages that happened during the function call that generated this error.
+ ///
+ protected AzureKinectGetDataBlockException(string message, ICollection logMessages)
+ : base(message, logMessages)
+ {
+ }
+
+ ///
+ /// Throws an if the result of the function
+ /// is not a success.
+ ///
+ /// The native function to call.
+ /// The type of result to expect from the function call.
+ internal static void ThrowIfNotSuccess(Func function)
+ where T : System.Enum
+ {
+ using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider))
+ {
+ T result = function();
+ if (!AzureKinectRecordException.IsSuccess(result))
+ {
+ throw new AzureKinectGetDataBlockException($"result = {result}", tracer.LogMessages);
+ }
+ }
+ }
+ }
+}
diff --git a/src/csharp/Record/Exceptions/AzureKinectGetImuSampleException.cs b/src/csharp/Record/Exceptions/AzureKinectGetImuSampleException.cs
new file mode 100644
index 000000000..089d6e49c
--- /dev/null
+++ b/src/csharp/Record/Exceptions/AzureKinectGetImuSampleException.cs
@@ -0,0 +1,101 @@
+//------------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+//
+//------------------------------------------------------------------------------
+using System;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions
+{
+ ///
+ /// Represents errors that occur when reading an IMU sample.
+ ///
+ [Serializable]
+ public class AzureKinectGetImuSampleException : AzureKinectRecordException
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AzureKinectGetImuSampleException()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ public AzureKinectGetImuSampleException(string message)
+ : base(message)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message and a reference to the inner exception that is the
+ /// cause of this exception.
+ ///
+ ///
+ /// The error message that explains the reason for the exception.
+ ///
+ ///
+ /// The exception that is the cause of the current exception, or a null reference
+ /// (Nothing in Visual Basic) if no inner exception is specified.
+ ///
+ public AzureKinectGetImuSampleException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with serialized data.
+ ///
+ ///
+ /// The that holds the serialized object data about the
+ /// exception being thrown.
+ ///
+ /// The System.Runtime.Serialization.StreamingContext that
+ /// contains contextual information about the source or destination.
+ ///
+ protected AzureKinectGetImuSampleException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ ///
+ /// The log messages that happened during the function call that generated this error.
+ ///
+ protected AzureKinectGetImuSampleException(string message, ICollection logMessages)
+ : base(message, logMessages)
+ {
+ }
+
+ ///
+ /// Throws an if the result of the function
+ /// is not a success.
+ ///
+ /// The native function to call.
+ /// The type of result to expect from the function call.
+ internal static void ThrowIfNotSuccess(Func function)
+ where T : System.Enum
+ {
+ using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider))
+ {
+ T result = function();
+ if (!AzureKinectRecordException.IsSuccess(result))
+ {
+ throw new AzureKinectGetImuSampleException($"result = {result}", tracer.LogMessages);
+ }
+ }
+ }
+ }
+}
diff --git a/src/csharp/Record/Exceptions/AzureKinectGetRawCalibrationException.cs b/src/csharp/Record/Exceptions/AzureKinectGetRawCalibrationException.cs
new file mode 100644
index 000000000..ba9795441
--- /dev/null
+++ b/src/csharp/Record/Exceptions/AzureKinectGetRawCalibrationException.cs
@@ -0,0 +1,101 @@
+//------------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+//
+//------------------------------------------------------------------------------
+using System;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions
+{
+ ///
+ /// Represents errors that occur when getting raw calibration from a recording.
+ ///
+ [Serializable]
+ public class AzureKinectGetRawCalibrationException : AzureKinectRecordException
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AzureKinectGetRawCalibrationException()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ public AzureKinectGetRawCalibrationException(string message)
+ : base(message)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message and a reference to the inner exception that is the
+ /// cause of this exception.
+ ///
+ ///
+ /// The error message that explains the reason for the exception.
+ ///
+ ///
+ /// The exception that is the cause of the current exception, or a null reference
+ /// (Nothing in Visual Basic) if no inner exception is specified.
+ ///
+ public AzureKinectGetRawCalibrationException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with serialized data.
+ ///
+ ///
+ /// The that holds the serialized object data about the
+ /// exception being thrown.
+ ///
+ /// The System.Runtime.Serialization.StreamingContext that
+ /// contains contextual information about the source or destination.
+ ///
+ protected AzureKinectGetRawCalibrationException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ ///
+ /// The log messages that happened during the function call that generated this error.
+ ///
+ protected AzureKinectGetRawCalibrationException(string message, ICollection logMessages)
+ : base(message, logMessages)
+ {
+ }
+
+ ///
+ /// Throws an if the result of the function
+ /// is not a success.
+ ///
+ /// The native function to call.
+ /// The type of result to expect from the function call.
+ internal static void ThrowIfNotSuccess(Func function)
+ where T : System.Enum
+ {
+ using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider))
+ {
+ T result = function();
+ if (!AzureKinectRecordException.IsSuccess(result))
+ {
+ throw new AzureKinectGetRawCalibrationException($"result = {result}", tracer.LogMessages);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/csharp/Record/Exceptions/AzureKinectGetTagException.cs b/src/csharp/Record/Exceptions/AzureKinectGetTagException.cs
new file mode 100644
index 000000000..7aea3d780
--- /dev/null
+++ b/src/csharp/Record/Exceptions/AzureKinectGetTagException.cs
@@ -0,0 +1,117 @@
+//------------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+//
+//------------------------------------------------------------------------------
+using System;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions
+{
+ ///
+ /// Represents errors that occur when getting a tag value.
+ ///
+ [Serializable]
+ public class AzureKinectGetTagException : AzureKinectRecordException
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AzureKinectGetTagException()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ public AzureKinectGetTagException(string message)
+ : base(message)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message and a reference to the inner exception that is the
+ /// cause of this exception.
+ ///
+ ///
+ /// The error message that explains the reason for the exception.
+ ///
+ ///
+ /// The exception that is the cause of the current exception, or a null reference
+ /// (Nothing in Visual Basic) if no inner exception is specified.
+ ///
+ public AzureKinectGetTagException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with serialized data.
+ ///
+ ///
+ /// The that holds the serialized object data about the
+ /// exception being thrown.
+ ///
+ /// The System.Runtime.Serialization.StreamingContext that
+ /// contains contextual information about the source or destination.
+ ///
+ protected AzureKinectGetTagException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ ///
+ /// The log messages that happened during the function call that generated this error.
+ ///
+ protected AzureKinectGetTagException(string message, ICollection logMessages)
+ : base(message, logMessages)
+ {
+ }
+
+ ///
+ /// Throws an if the result of the function
+ /// is not a success.
+ ///
+ /// The native function to call.
+ /// The type of result to expect from the function call.
+ internal static void ThrowIfNotSuccess(Func function)
+ where T : System.Enum
+ {
+ using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider))
+ {
+ T result = function();
+ if (!AzureKinectRecordException.IsSuccess(result))
+ {
+ throw new AzureKinectGetTagException($"result = {result}", tracer.LogMessages);
+ }
+ }
+ }
+
+ ///
+ /// Throws an if the result of the function is not
+ /// a success.
+ ///
+ /// The tracer is that is capturing logging messages.
+ /// The result native function to call.
+ /// The type of result to expect from the function call.
+ internal static void ThrowIfNotSuccess(LoggingTracer tracer, T result)
+ where T : System.Enum
+ {
+ if (!AzureKinectRecordException.IsSuccess(result))
+ {
+ throw new AzureKinectGetTagException($"result = {result}", tracer.LogMessages);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/csharp/Record/Exceptions/AzureKinectGetTrackCodecContextException.cs b/src/csharp/Record/Exceptions/AzureKinectGetTrackCodecContextException.cs
new file mode 100644
index 000000000..e5f359115
--- /dev/null
+++ b/src/csharp/Record/Exceptions/AzureKinectGetTrackCodecContextException.cs
@@ -0,0 +1,101 @@
+//------------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+//
+//------------------------------------------------------------------------------
+using System;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions
+{
+ ///
+ /// Represents errors that occur when getting a codec context from a track.
+ ///
+ [Serializable]
+ public class AzureKinectGetTrackCodecContextException : AzureKinectRecordException
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AzureKinectGetTrackCodecContextException()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ public AzureKinectGetTrackCodecContextException(string message)
+ : base(message)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message and a reference to the inner exception that is the
+ /// cause of this exception.
+ ///
+ ///
+ /// The error message that explains the reason for the exception.
+ ///
+ ///
+ /// The exception that is the cause of the current exception, or a null reference
+ /// (Nothing in Visual Basic) if no inner exception is specified.
+ ///
+ public AzureKinectGetTrackCodecContextException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with serialized data.
+ ///
+ ///
+ /// The that holds the serialized object data about the
+ /// exception being thrown.
+ ///
+ /// The System.Runtime.Serialization.StreamingContext that
+ /// contains contextual information about the source or destination.
+ ///
+ protected AzureKinectGetTrackCodecContextException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ ///
+ /// The log messages that happened during the function call that generated this error.
+ ///
+ protected AzureKinectGetTrackCodecContextException(string message, ICollection logMessages)
+ : base(message, logMessages)
+ {
+ }
+
+ ///
+ /// Throws an if the result of the function
+ /// is not a success.
+ ///
+ /// The native function to call.
+ /// The type of result to expect from the function call.
+ internal static void ThrowIfNotSuccess(Func function)
+ where T : System.Enum
+ {
+ using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider))
+ {
+ T result = function();
+ if (!AzureKinectRecordException.IsSuccess(result))
+ {
+ throw new AzureKinectGetTrackCodecContextException($"result = {result}", tracer.LogMessages);
+ }
+ }
+ }
+ }
+}
diff --git a/src/csharp/Record/Exceptions/AzureKinectGetTrackNameException.cs b/src/csharp/Record/Exceptions/AzureKinectGetTrackNameException.cs
new file mode 100644
index 000000000..609089873
--- /dev/null
+++ b/src/csharp/Record/Exceptions/AzureKinectGetTrackNameException.cs
@@ -0,0 +1,101 @@
+//------------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+//
+//------------------------------------------------------------------------------
+using System;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions
+{
+ ///
+ /// Represents errors that occur when getting a track name.
+ ///
+ [Serializable]
+ public class AzureKinectGetTrackNameException : AzureKinectRecordException
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AzureKinectGetTrackNameException()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ public AzureKinectGetTrackNameException(string message)
+ : base(message)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message and a reference to the inner exception that is the
+ /// cause of this exception.
+ ///
+ ///
+ /// The error message that explains the reason for the exception.
+ ///
+ ///
+ /// The exception that is the cause of the current exception, or a null reference
+ /// (Nothing in Visual Basic) if no inner exception is specified.
+ ///
+ public AzureKinectGetTrackNameException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with serialized data.
+ ///
+ ///
+ /// The that holds the serialized object data about the
+ /// exception being thrown.
+ ///
+ /// The System.Runtime.Serialization.StreamingContext that
+ /// contains contextual information about the source or destination.
+ ///
+ protected AzureKinectGetTrackNameException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ ///
+ /// The log messages that happened during the function call that generated this error.
+ ///
+ protected AzureKinectGetTrackNameException(string message, ICollection logMessages)
+ : base(message, logMessages)
+ {
+ }
+
+ ///
+ /// Throws an if the result of the function
+ /// is not a success.
+ ///
+ /// The native function to call.
+ /// The type of result to expect from the function call.
+ internal static void ThrowIfNotSuccess(Func function)
+ where T : System.Enum
+ {
+ using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider))
+ {
+ T result = function();
+ if (!AzureKinectRecordException.IsSuccess(result))
+ {
+ throw new AzureKinectGetTrackNameException($"result = {result}", tracer.LogMessages);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/csharp/Record/Exceptions/AzureKinectOpenPlaybackException.cs b/src/csharp/Record/Exceptions/AzureKinectOpenPlaybackException.cs
new file mode 100644
index 000000000..c098844cc
--- /dev/null
+++ b/src/csharp/Record/Exceptions/AzureKinectOpenPlaybackException.cs
@@ -0,0 +1,101 @@
+//------------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+//
+//------------------------------------------------------------------------------
+using System;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions
+{
+ ///
+ /// Represents errors that occur when opening a recording for playback.
+ ///
+ [Serializable]
+ public class AzureKinectOpenPlaybackException : AzureKinectRecordException
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AzureKinectOpenPlaybackException()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ public AzureKinectOpenPlaybackException(string message)
+ : base(message)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message and a reference to the inner exception that is the
+ /// cause of this exception.
+ ///
+ ///
+ /// The error message that explains the reason for the exception.
+ ///
+ ///
+ /// The exception that is the cause of the current exception, or a null reference
+ /// (Nothing in Visual Basic) if no inner exception is specified.
+ ///
+ public AzureKinectOpenPlaybackException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with serialized data.
+ ///
+ ///
+ /// The that holds the serialized object data about the
+ /// exception being thrown.
+ ///
+ /// The System.Runtime.Serialization.StreamingContext that
+ /// contains contextual information about the source or destination.
+ ///
+ protected AzureKinectOpenPlaybackException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ ///
+ /// The log messages that happened during the function call that generated this error.
+ ///
+ protected AzureKinectOpenPlaybackException(string message, ICollection logMessages)
+ : base(message, logMessages)
+ {
+ }
+
+ ///
+ /// Throws an if the result of the function
+ /// is not a success.
+ ///
+ /// The native function to call.
+ /// The type of result to expect from the function call.
+ internal static void ThrowIfNotSuccess(Func function)
+ where T : System.Enum
+ {
+ using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider))
+ {
+ T result = function();
+ if (!AzureKinectRecordException.IsSuccess(result))
+ {
+ throw new AzureKinectOpenPlaybackException($"result = {result}", tracer.LogMessages);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/csharp/Record/Exceptions/AzureKinectRecordException.cs b/src/csharp/Record/Exceptions/AzureKinectRecordException.cs
new file mode 100644
index 000000000..5f7404e81
--- /dev/null
+++ b/src/csharp/Record/Exceptions/AzureKinectRecordException.cs
@@ -0,0 +1,150 @@
+//------------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+//
+//------------------------------------------------------------------------------
+using System;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions
+{
+ ///
+ /// Represents errors occuring during record or playback.
+ ///
+ [Serializable]
+ public abstract class AzureKinectRecordException : AzureKinectException
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ protected AzureKinectRecordException()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ protected AzureKinectRecordException(string message)
+ : base(message)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message and a reference to the inner exception that is the
+ /// cause of this exception.
+ ///
+ ///
+ /// The error message that explains the reason for the exception.
+ ///
+ ///
+ /// The exception that is the cause of the current exception, or a null reference
+ /// (Nothing in Visual Basic) if no inner exception is specified.
+ ///
+ protected AzureKinectRecordException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with serialized data.
+ ///
+ ///
+ /// The that holds the serialized object data about the
+ /// exception being thrown.
+ ///
+ /// The System.Runtime.Serialization.StreamingContext that
+ /// contains contextual information about the source or destination.
+ ///
+ protected AzureKinectRecordException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ ///
+ /// The log messages that happened during the function call that generated this error.
+ ///
+ protected AzureKinectRecordException(string message, ICollection logMessages)
+ : base(message, logMessages)
+ {
+ }
+
+ ///
+ /// Determines if the result is a success result.
+ ///
+ /// The type of result.
+ /// The result to check if it is a success.
+ /// True if the result is a success;otherwise false.
+ internal static bool IsSuccess(T result)
+ where T : Enum
+ {
+ switch (result)
+ {
+ case NativeMethods.k4a_result_t k4a_result:
+ return IsSuccess(k4a_result);
+
+ case NativeMethods.k4a_wait_result_t k4a_result:
+ return IsSuccess(k4a_result);
+
+ case NativeMethods.k4a_buffer_result_t k4a_result:
+ return IsSuccess(k4a_result);
+
+ case NativeMethods.k4a_stream_result_t k4a_result:
+ return IsSuccess(k4a_result);
+
+ default:
+ throw new ArgumentException("Result is not of a recognized result type.", nameof(result));
+ }
+ }
+
+ ///
+ /// Determines if the is a success.
+ ///
+ /// The result to check if it is a success.
+ /// True if the result is a success;otherwise false.
+ internal static bool IsSuccess(NativeMethods.k4a_result_t result)
+ {
+ return result == NativeMethods.k4a_result_t.K4A_RESULT_SUCCEEDED;
+ }
+
+ ///
+ /// Determines if the is a success.
+ ///
+ /// The result to check if it is a success.
+ /// True if the result is a success;otherwise false.
+ internal static bool IsSuccess(NativeMethods.k4a_wait_result_t result)
+ {
+ return result == NativeMethods.k4a_wait_result_t.K4A_WAIT_RESULT_SUCCEEDED;
+ }
+
+ ///
+ /// Determines if the is a success.
+ ///
+ /// The result to check if it is a success.
+ /// True if the result is a success;otherwise false.
+ internal static bool IsSuccess(NativeMethods.k4a_buffer_result_t result)
+ {
+ return result == NativeMethods.k4a_buffer_result_t.K4A_BUFFER_RESULT_SUCCEEDED;
+ }
+
+ ///
+ /// Determines if the is a success.
+ ///
+ /// The result to check if it is a success.
+ /// True if the result is a success;otherwise false.
+ internal static bool IsSuccess(NativeMethods.k4a_stream_result_t result)
+ {
+ return result == NativeMethods.k4a_stream_result_t.K4A_STREAM_RESULT_SUCCEEDED;
+ }
+ }
+}
diff --git a/src/csharp/Record/Exceptions/AzureKinectSeekException.cs b/src/csharp/Record/Exceptions/AzureKinectSeekException.cs
new file mode 100644
index 000000000..1c91f5abc
--- /dev/null
+++ b/src/csharp/Record/Exceptions/AzureKinectSeekException.cs
@@ -0,0 +1,101 @@
+//------------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+//
+//------------------------------------------------------------------------------
+using System;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions
+{
+ ///
+ /// Represents errors that occur when seeking.
+ ///
+ [Serializable]
+ public class AzureKinectSeekException : AzureKinectRecordException
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AzureKinectSeekException()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ public AzureKinectSeekException(string message)
+ : base(message)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message and a reference to the inner exception that is the
+ /// cause of this exception.
+ ///
+ ///
+ /// The error message that explains the reason for the exception.
+ ///
+ ///
+ /// The exception that is the cause of the current exception, or a null reference
+ /// (Nothing in Visual Basic) if no inner exception is specified.
+ ///
+ public AzureKinectSeekException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with serialized data.
+ ///
+ ///
+ /// The that holds the serialized object data about the
+ /// exception being thrown.
+ ///
+ /// The System.Runtime.Serialization.StreamingContext that
+ /// contains contextual information about the source or destination.
+ ///
+ protected AzureKinectSeekException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ ///
+ /// The log messages that happened during the function call that generated this error.
+ ///
+ protected AzureKinectSeekException(string message, ICollection logMessages)
+ : base(message, logMessages)
+ {
+ }
+
+ ///
+ /// Throws an if the result of the function
+ /// is not a success.
+ ///
+ /// The native function to call.
+ /// The type of result to expect from the function call.
+ internal static void ThrowIfNotSuccess(Func function)
+ where T : System.Enum
+ {
+ using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider))
+ {
+ T result = function();
+ if (!AzureKinectRecordException.IsSuccess(result))
+ {
+ throw new AzureKinectSeekException($"result = {result}", tracer.LogMessages);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/csharp/Record/Exceptions/AzureKinectSetColorConversionException.cs b/src/csharp/Record/Exceptions/AzureKinectSetColorConversionException.cs
new file mode 100644
index 000000000..8f9c6d728
--- /dev/null
+++ b/src/csharp/Record/Exceptions/AzureKinectSetColorConversionException.cs
@@ -0,0 +1,101 @@
+//------------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+//
+//------------------------------------------------------------------------------
+using System;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions
+{
+ ///
+ /// Represents errors that occur when setting a color conversion on playback.
+ ///
+ [Serializable]
+ public class AzureKinectSetColorConversionException : AzureKinectRecordException
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AzureKinectSetColorConversionException()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ public AzureKinectSetColorConversionException(string message)
+ : base(message)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message and a reference to the inner exception that is the
+ /// cause of this exception.
+ ///
+ ///
+ /// The error message that explains the reason for the exception.
+ ///
+ ///
+ /// The exception that is the cause of the current exception, or a null reference
+ /// (Nothing in Visual Basic) if no inner exception is specified.
+ ///
+ public AzureKinectSetColorConversionException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with serialized data.
+ ///
+ ///
+ /// The that holds the serialized object data about the
+ /// exception being thrown.
+ ///
+ /// The System.Runtime.Serialization.StreamingContext that
+ /// contains contextual information about the source or destination.
+ ///
+ protected AzureKinectSetColorConversionException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ ///
+ /// The log messages that happened during the function call that generated this error.
+ ///
+ protected AzureKinectSetColorConversionException(string message, ICollection logMessages)
+ : base(message, logMessages)
+ {
+ }
+
+ ///
+ /// Throws an if the result of the function
+ /// is not a success.
+ ///
+ /// The native function to call.
+ /// The type of result to expect from the function call.
+ internal static void ThrowIfNotSuccess(Func function)
+ where T : System.Enum
+ {
+ using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider))
+ {
+ T result = function();
+ if (!AzureKinectRecordException.IsSuccess(result))
+ {
+ throw new AzureKinectSetColorConversionException($"result = {result}", tracer.LogMessages);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/csharp/Record/Exceptions/AzureKinectTrackGetVideoSettingsException.cs b/src/csharp/Record/Exceptions/AzureKinectTrackGetVideoSettingsException.cs
new file mode 100644
index 000000000..4e3f10254
--- /dev/null
+++ b/src/csharp/Record/Exceptions/AzureKinectTrackGetVideoSettingsException.cs
@@ -0,0 +1,101 @@
+//------------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+//
+//------------------------------------------------------------------------------
+using System;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions
+{
+ ///
+ /// Represents errors that occur when an error occurs getting track video settings.
+ ///
+ [Serializable]
+ public class AzureKinectTrackGetVideoSettingsException : AzureKinectRecordException
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AzureKinectTrackGetVideoSettingsException()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ public AzureKinectTrackGetVideoSettingsException(string message)
+ : base(message)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message and a reference to the inner exception that is the
+ /// cause of this exception.
+ ///
+ ///
+ /// The error message that explains the reason for the exception.
+ ///
+ ///
+ /// The exception that is the cause of the current exception, or a null reference
+ /// (Nothing in Visual Basic) if no inner exception is specified.
+ ///
+ public AzureKinectTrackGetVideoSettingsException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with serialized data.
+ ///
+ ///
+ /// The that holds the serialized object data about the
+ /// exception being thrown.
+ ///
+ /// The System.Runtime.Serialization.StreamingContext that
+ /// contains contextual information about the source or destination.
+ ///
+ protected AzureKinectTrackGetVideoSettingsException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ ///
+ /// The log messages that happened during the function call that generated this error.
+ ///
+ protected AzureKinectTrackGetVideoSettingsException(string message, ICollection logMessages)
+ : base(message, logMessages)
+ {
+ }
+
+ ///
+ /// Throws an if the result of the function
+ /// is not a success.
+ ///
+ /// The native function to call.
+ /// The type of result to expect from the function call.
+ internal static void ThrowIfNotSuccess(Func function)
+ where T : System.Enum
+ {
+ using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider))
+ {
+ T result = function();
+ if (!AzureKinectRecordException.IsSuccess(result))
+ {
+ throw new AzureKinectTrackGetVideoSettingsException("result = {result}", tracer.LogMessages);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/csharp/Record/Exceptions/AzureKinectWriteCaptureException.cs b/src/csharp/Record/Exceptions/AzureKinectWriteCaptureException.cs
new file mode 100644
index 000000000..a7be4bcab
--- /dev/null
+++ b/src/csharp/Record/Exceptions/AzureKinectWriteCaptureException.cs
@@ -0,0 +1,101 @@
+//------------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+//
+//------------------------------------------------------------------------------
+using System;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions
+{
+ ///
+ /// Represents errors that occur when writing a capture to a recording.
+ ///
+ [Serializable]
+ public class AzureKinectWriteCaptureException : AzureKinectRecordException
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AzureKinectWriteCaptureException()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ public AzureKinectWriteCaptureException(string message)
+ : base(message)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message and a reference to the inner exception that is the
+ /// cause of this exception.
+ ///
+ ///
+ /// The error message that explains the reason for the exception.
+ ///
+ ///
+ /// The exception that is the cause of the current exception, or a null reference
+ /// (Nothing in Visual Basic) if no inner exception is specified.
+ ///
+ public AzureKinectWriteCaptureException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with serialized data.
+ ///
+ ///
+ /// The that holds the serialized object data about the
+ /// exception being thrown.
+ ///
+ /// The System.Runtime.Serialization.StreamingContext that
+ /// contains contextual information about the source or destination.
+ ///
+ protected AzureKinectWriteCaptureException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ ///
+ /// The log messages that happened during the function call that generated this error.
+ ///
+ protected AzureKinectWriteCaptureException(string message, ICollection logMessages)
+ : base(message, logMessages)
+ {
+ }
+
+ ///
+ /// Throws an if the result of the function
+ /// is not a success.
+ ///
+ /// The native function to call.
+ /// The type of result to expect from the function call.
+ internal static void ThrowIfNotSuccess(Func function)
+ where T : System.Enum
+ {
+ using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider))
+ {
+ T result = function();
+ if (!AzureKinectRecordException.IsSuccess(result))
+ {
+ throw new AzureKinectWriteCaptureException($"result = {result}", tracer.LogMessages);
+ }
+ }
+ }
+ }
+}
diff --git a/src/csharp/Record/Exceptions/AzureKinectWriteCustomTrackDataException.cs b/src/csharp/Record/Exceptions/AzureKinectWriteCustomTrackDataException.cs
new file mode 100644
index 000000000..8fece967e
--- /dev/null
+++ b/src/csharp/Record/Exceptions/AzureKinectWriteCustomTrackDataException.cs
@@ -0,0 +1,101 @@
+//------------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+//
+//------------------------------------------------------------------------------
+using System;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions
+{
+ ///
+ /// Represents errors that occur when writing a custom track.
+ ///
+ [Serializable]
+ public class AzureKinectWriteCustomTrackDataException : AzureKinectRecordException
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AzureKinectWriteCustomTrackDataException()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ public AzureKinectWriteCustomTrackDataException(string message)
+ : base(message)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message and a reference to the inner exception that is the
+ /// cause of this exception.
+ ///
+ ///
+ /// The error message that explains the reason for the exception.
+ ///
+ ///
+ /// The exception that is the cause of the current exception, or a null reference
+ /// (Nothing in Visual Basic) if no inner exception is specified.
+ ///
+ public AzureKinectWriteCustomTrackDataException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with serialized data.
+ ///
+ ///
+ /// The that holds the serialized object data about the
+ /// exception being thrown.
+ ///
+ /// The System.Runtime.Serialization.StreamingContext that
+ /// contains contextual information about the source or destination.
+ ///
+ protected AzureKinectWriteCustomTrackDataException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ ///
+ /// The log messages that happened during the function call that generated this error.
+ ///
+ protected AzureKinectWriteCustomTrackDataException(string message, ICollection logMessages)
+ : base(message, logMessages)
+ {
+ }
+
+ ///
+ /// Throws an if the result of the function
+ /// is not a success.
+ ///
+ /// The native function to call.
+ /// The type of result to expect from the function call.
+ internal static void ThrowIfNotSuccess(Func function)
+ where T : System.Enum
+ {
+ using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider))
+ {
+ T result = function();
+ if (!AzureKinectRecordException.IsSuccess(result))
+ {
+ throw new AzureKinectWriteCustomTrackDataException($"result = {result}", tracer.LogMessages);
+ }
+ }
+ }
+ }
+}
diff --git a/src/csharp/Record/Exceptions/AzureKinectWriteHeaderException.cs b/src/csharp/Record/Exceptions/AzureKinectWriteHeaderException.cs
new file mode 100644
index 000000000..1d3277753
--- /dev/null
+++ b/src/csharp/Record/Exceptions/AzureKinectWriteHeaderException.cs
@@ -0,0 +1,101 @@
+//------------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+//
+//------------------------------------------------------------------------------
+using System;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions
+{
+ ///
+ /// Represents errors that occur when writing the header.
+ ///
+ [Serializable]
+ public class AzureKinectWriteHeaderException : AzureKinectRecordException
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AzureKinectWriteHeaderException()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ public AzureKinectWriteHeaderException(string message)
+ : base(message)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message and a reference to the inner exception that is the
+ /// cause of this exception.
+ ///
+ ///
+ /// The error message that explains the reason for the exception.
+ ///
+ ///
+ /// The exception that is the cause of the current exception, or a null reference
+ /// (Nothing in Visual Basic) if no inner exception is specified.
+ ///
+ public AzureKinectWriteHeaderException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with serialized data.
+ ///
+ ///
+ /// The that holds the serialized object data about the
+ /// exception being thrown.
+ ///
+ /// The System.Runtime.Serialization.StreamingContext that
+ /// contains contextual information about the source or destination.
+ ///
+ protected AzureKinectWriteHeaderException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ ///
+ /// The log messages that happened during the function call that generated this error.
+ ///
+ protected AzureKinectWriteHeaderException(string message, ICollection logMessages)
+ : base(message, logMessages)
+ {
+ }
+
+ ///
+ /// Throws an if the result of the function
+ /// is not a success.
+ ///
+ /// The native function to call.
+ /// The type of result to expect from the function call.
+ internal static void ThrowIfNotSuccess(Func function)
+ where T : System.Enum
+ {
+ using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider))
+ {
+ T result = function();
+ if (!AzureKinectRecordException.IsSuccess(result))
+ {
+ throw new AzureKinectWriteHeaderException("result = {result}", tracer.LogMessages);
+ }
+ }
+ }
+ }
+}
diff --git a/src/csharp/Record/Exceptions/AzureKinectWriteImuSampleException.cs b/src/csharp/Record/Exceptions/AzureKinectWriteImuSampleException.cs
new file mode 100644
index 000000000..982843ab2
--- /dev/null
+++ b/src/csharp/Record/Exceptions/AzureKinectWriteImuSampleException.cs
@@ -0,0 +1,101 @@
+//------------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+//
+//------------------------------------------------------------------------------
+using System;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions
+{
+ ///
+ /// Represents errors that occur when writing an IMU sample.
+ ///
+ [Serializable]
+ public class AzureKinectWriteImuSampleException : AzureKinectRecordException
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AzureKinectWriteImuSampleException()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ public AzureKinectWriteImuSampleException(string message)
+ : base(message)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message and a reference to the inner exception that is the
+ /// cause of this exception.
+ ///
+ ///
+ /// The error message that explains the reason for the exception.
+ ///
+ ///
+ /// The exception that is the cause of the current exception, or a null reference
+ /// (Nothing in Visual Basic) if no inner exception is specified.
+ ///
+ public AzureKinectWriteImuSampleException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with serialized data.
+ ///
+ ///
+ /// The that holds the serialized object data about the
+ /// exception being thrown.
+ ///
+ /// The System.Runtime.Serialization.StreamingContext that
+ /// contains contextual information about the source or destination.
+ ///
+ protected AzureKinectWriteImuSampleException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ ///
+ /// The log messages that happened during the function call that generated this error.
+ ///
+ protected AzureKinectWriteImuSampleException(string message, ICollection logMessages)
+ : base(message, logMessages)
+ {
+ }
+
+ ///
+ /// Throws an if the result of the function
+ /// is not a success.
+ ///
+ /// The native function to call.
+ /// The type of result to expect from the function call.
+ internal static void ThrowIfNotSuccess(Func function)
+ where T : System.Enum
+ {
+ using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider))
+ {
+ T result = function();
+ if (!AzureKinectRecordException.IsSuccess(result))
+ {
+ throw new AzureKinectWriteImuSampleException($"result = {result}", tracer.LogMessages);
+ }
+ }
+ }
+ }
+}
diff --git a/src/csharp/Record/Microsoft.Azure.Kinect.Sensor.Record.csproj b/src/csharp/Record/Microsoft.Azure.Kinect.Sensor.Record.csproj
new file mode 100644
index 000000000..7a462b98f
--- /dev/null
+++ b/src/csharp/Record/Microsoft.Azure.Kinect.Sensor.Record.csproj
@@ -0,0 +1,46 @@
+
+
+
+
+ netstandard2.0
+ latest
+
+ false
+ ..\AzureKinectSensorSDK.ruleset
+ $(BaseOutputPath)\$(AssemblyName)\
+ AnyCPU;x64
+
+
+
+ true
+ $(OutputPath)Microsoft.Azure.Kinect.Sensor.xml
+ true
+ ..\Microsoft.Azure.Kinect.Sensor.snk
+
+
+
+
+ stylecop.json
+
+
+
+
+
+
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
diff --git a/src/csharp/Record/NativeMethods.cs b/src/csharp/Record/NativeMethods.cs
new file mode 100644
index 000000000..657f47f6d
--- /dev/null
+++ b/src/csharp/Record/NativeMethods.cs
@@ -0,0 +1,369 @@
+//------------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+//
+//------------------------------------------------------------------------------
+using System;
+using System.Numerics;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace Microsoft.Azure.Kinect.Sensor.Record
+{
+#pragma warning disable IDE1006 // Naming Styles
+#pragma warning disable SA1600 // Elements should be documented
+#pragma warning disable SA1602 // Enumeration items should be documented
+#pragma warning disable CA2101 // Specify marshaling for P/Invoke string arguments
+ internal static class NativeMethods
+ {
+ private const CallingConvention k4aCallingConvention = CallingConvention.Cdecl;
+
+ [UnmanagedFunctionPointer(k4aCallingConvention)]
+ public delegate void k4a_logging_message_cb_t(IntPtr context, LogLevel level, [MarshalAs(UnmanagedType.LPStr)] string file, int line, [MarshalAs(UnmanagedType.LPStr)] string message);
+
+ public enum k4a_buffer_result_t
+ {
+ K4A_BUFFER_RESULT_SUCCEEDED = 0,
+ K4A_BUFFER_RESULT_FAILED,
+ K4A_BUFFER_RESULT_TOO_SMALL,
+ }
+
+ public enum k4a_wait_result_t
+ {
+ K4A_WAIT_RESULT_SUCCEEDED = 0,
+ K4A_WAIT_RESULT_FAILED,
+ K4A_WAIT_RESULT_TIMEOUT,
+ }
+
+ public enum k4a_result_t
+ {
+ K4A_RESULT_SUCCEEDED = 0,
+ K4A_RESULT_FAILED,
+ }
+
+ public enum k4a_stream_result_t
+ {
+ K4A_STREAM_RESULT_SUCCEEDED = 0,
+ K4A_STREAM_RESULT_FAILED,
+ K4A_STREAM_RESULT_EOF,
+ }
+
+ [DllImport("k4arecord", CallingConvention = k4aCallingConvention)]
+ public static extern k4a_result_t k4a_record_set_debug_message_handler(
+ k4a_logging_message_cb_t message_cb,
+ IntPtr message_cb_context,
+ LogLevel min_level);
+
+ [DllImport("k4arecord", CallingConvention = k4aCallingConvention, CharSet = CharSet.Ansi)]
+ public static extern k4a_result_t k4a_record_create(string path, IntPtr device, k4a_device_configuration_t deviceConfiguration, out k4a_record_t handle);
+
+ [DllImport("k4arecord", CallingConvention = k4aCallingConvention, CharSet = CharSet.Ansi)]
+ public static extern k4a_result_t k4a_record_add_tag(k4a_record_t handle, string name, string value);
+
+ [DllImport("k4arecord", CallingConvention = k4aCallingConvention, CharSet = CharSet.Ansi)]
+ public static extern k4a_result_t k4a_record_add_imu_track(k4a_record_t handle);
+
+ [DllImport("k4arecord", CallingConvention = k4aCallingConvention, CharSet = CharSet.Ansi)]
+ public static extern k4a_result_t k4a_record_add_attachment(k4a_record_t handle, string attachment_name, byte[] buffer, UIntPtr buffer_size);
+
+ [DllImport("k4arecord", CallingConvention = k4aCallingConvention, CharSet = CharSet.Ansi)]
+ public static extern k4a_result_t k4a_record_add_custom_video_track(k4a_record_t handle, string track_name, string codec_id, byte[] codec_context, UIntPtr codec_context_size, RecordVideoSettings track_settings);
+
+ [DllImport("k4arecord", CallingConvention = k4aCallingConvention, CharSet = CharSet.Ansi)]
+ public static extern k4a_result_t k4a_record_add_custom_subtitle_track(k4a_record_t handle, string track_name, string codec_id, byte[] codec_context, UIntPtr codec_context_size, RecordSubtitleSettings track_settings);
+
+ [DllImport("k4arecord", CallingConvention = k4aCallingConvention)]
+ public static extern k4a_result_t k4a_record_write_header(k4a_record_t handle);
+
+ [DllImport("k4arecord", CallingConvention = k4aCallingConvention)]
+ public static extern k4a_result_t k4a_record_write_capture(k4a_record_t handle, IntPtr capture);
+
+ [DllImport("k4arecord", CallingConvention = k4aCallingConvention)]
+ public static extern k4a_result_t k4a_record_write_imu_sample(k4a_record_t handle, k4a_imu_sample_t imu_sample);
+
+ [DllImport("k4arecord", CallingConvention = k4aCallingConvention)]
+ public static extern k4a_result_t k4a_record_write_custom_track_data(k4a_record_t handle, string track_name, ulong device_timestamp_usec, byte[] custom_data, UIntPtr custom_data_size);
+
+ [DllImport("k4arecord", CallingConvention = k4aCallingConvention)]
+ public static extern k4a_result_t k4a_record_flush(k4a_record_t handle);
+
+ [DllImport("k4arecord", CallingConvention = k4aCallingConvention)]
+ public static extern void k4a_record_close(IntPtr handle);
+
+ [DllImport("k4arecord", CallingConvention = k4aCallingConvention)]
+ public static extern k4a_result_t k4a_playback_open(string path, out k4a_playback_t handle);
+
+ [DllImport("k4arecord", CallingConvention = k4aCallingConvention)]
+ public static extern k4a_buffer_result_t k4a_playback_get_raw_calibration(k4a_playback_t handle, byte[] data, ref UIntPtr data_size);
+
+ [DllImport("k4arecord", CallingConvention = k4aCallingConvention)]
+ public static extern k4a_result_t k4a_playback_get_calibration(k4a_playback_t playback_handle, out Calibration calibration);
+
+ [DllImport("k4arecord", CallingConvention = k4aCallingConvention)]
+ public static extern k4a_result_t k4a_playback_get_record_configuration(k4a_playback_t playback_handle, ref k4a_record_configuration_t configuration);
+
+ [DllImport("k4arecord", CallingConvention = k4aCallingConvention)]
+ public static extern bool k4a_playback_check_track_exists(k4a_playback_t playback_handle, string track_name);
+
+ [DllImport("k4arecord", CallingConvention = k4aCallingConvention)]
+ public static extern UIntPtr k4a_playback_get_track_count(k4a_playback_t playback_handle);
+
+ [DllImport("k4arecord", CallingConvention = k4aCallingConvention)]
+ public static extern k4a_buffer_result_t k4a_playback_get_track_name(k4a_playback_t playback_handle, UIntPtr track_index, StringBuilder track_name, ref UIntPtr track_name_size);
+
+ [DllImport("k4arecord", CallingConvention = k4aCallingConvention)]
+ public static extern bool k4a_playback_track_is_builtin(k4a_playback_t playback_handle, string track_name);
+
+ [DllImport("k4arecord", CallingConvention = k4aCallingConvention)]
+ public static extern k4a_result_t k4a_playback_track_get_video_settings(k4a_playback_t playback_handle, string track_name, out RecordVideoSettings video_settings);
+
+ [DllImport("k4arecord", CallingConvention = k4aCallingConvention)]
+ public static extern k4a_buffer_result_t k4a_playback_track_get_codec_id(k4a_playback_t playback_handle, string track_name, StringBuilder codec_id, ref UIntPtr codec_id_size);
+
+ [DllImport("k4arecord", CallingConvention = k4aCallingConvention)]
+ public static extern k4a_buffer_result_t k4a_playback_track_get_codec_context(
+ k4a_playback_t playback_handle,
+ string track_name,
+ byte[] codec_context,
+ ref UIntPtr codec_context_size);
+
+ [DllImport("k4arecord", CallingConvention = k4aCallingConvention, CharSet = CharSet.Ansi)]
+ public static extern k4a_buffer_result_t k4a_playback_get_tag(
+ k4a_playback_t playback_handle,
+ string track_name,
+ StringBuilder value,
+ ref UIntPtr codec_context_size);
+
+ [DllImport("k4arecord", CallingConvention = k4aCallingConvention)]
+ public static extern k4a_result_t k4a_playback_set_color_conversion(
+ k4a_playback_t playback_handle,
+ ImageFormat target_format);
+
+ [DllImport("k4arecord", CallingConvention = k4aCallingConvention, CharSet = CharSet.Ansi)]
+ public static extern k4a_buffer_result_t k4a_playback_get_attachment(
+ k4a_playback_t playback_handle,
+ string file_name,
+ byte[] data,
+ ref UIntPtr data_size);
+
+ [DllImport("k4arecord", CallingConvention = k4aCallingConvention)]
+ public static extern k4a_stream_result_t k4a_playback_get_next_capture(
+ k4a_playback_t playback_handle,
+ out IntPtr capture_handle);
+
+ [DllImport("k4arecord", CallingConvention = k4aCallingConvention)]
+ public static extern k4a_stream_result_t k4a_playback_get_previous_capture(
+ k4a_playback_t playback_handle,
+ out IntPtr capture_handle);
+
+ [DllImport("k4arecord", CallingConvention = k4aCallingConvention)]
+ public static extern k4a_stream_result_t k4a_playback_get_next_imu_sample(
+ k4a_playback_t playback_handle,
+ [Out] k4a_imu_sample_t imu_sample);
+
+ [DllImport("k4arecord", CallingConvention = k4aCallingConvention)]
+ public static extern k4a_stream_result_t k4a_playback_get_previous_imu_sample(
+ k4a_playback_t playback_handle,
+ [Out] k4a_imu_sample_t imu_sample);
+
+ [DllImport("k4arecord", CallingConvention = k4aCallingConvention)]
+ public static extern k4a_stream_result_t k4a_playback_get_next_data_block(
+ k4a_playback_t playback_handle,
+ string track_name,
+ out k4a_playback_data_block_t data_block);
+
+ [DllImport("k4arecord", CallingConvention = k4aCallingConvention)]
+ public static extern k4a_stream_result_t k4a_playback_get_previous_data_block(
+ k4a_playback_t playback_handle,
+ string track_name,
+ out k4a_playback_data_block_t data_block_handle);
+
+ [DllImport("k4arecord", CallingConvention = k4aCallingConvention)]
+ public static extern IntPtr k4a_playback_data_block_get_buffer(k4a_playback_data_block_t data_block_handle);
+
+ [DllImport("k4arecord", CallingConvention = k4aCallingConvention)]
+ public static extern ulong k4a_playback_data_block_get_device_timestamp_usec(k4a_playback_data_block_t data_block_handle);
+
+ [DllImport("k4arecord", CallingConvention = k4aCallingConvention)]
+ public static extern ulong k4a_playback_data_block_get_buffer_size(k4a_playback_data_block_t data_block_handle);
+
+ [DllImport("k4arecord", CallingConvention = k4aCallingConvention)]
+ public static extern void k4a_playback_data_block_release(IntPtr data_block_handle);
+
+ [DllImport("k4arecord", CallingConvention = k4aCallingConvention)]
+ public static extern k4a_result_t k4a_playback_seek_timestamp(k4a_playback_t playback_handle, ulong offset_usec, PlaybackSeekOrigin origin);
+
+ [DllImport("k4arecord", CallingConvention = k4aCallingConvention)]
+ public static extern ulong k4a_playback_get_recording_length_usec(k4a_playback_t playback_handle);
+
+ [DllImport("k4arecord", CallingConvention = k4aCallingConvention)]
+ public static extern ulong k4a_playback_get_last_timestamp_usec(k4a_playback_t playback_handle);
+
+ [DllImport("k4arecord", CallingConvention = k4aCallingConvention)]
+ public static extern void k4a_playback_close(IntPtr playback_handle);
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct k4a_version_t
+ {
+ public int major;
+ public int minor;
+ public int revision;
+
+ public Version ToVersion()
+ {
+ return new Version(this.major, this.minor, this.revision);
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct k4a_hardware_version_t
+ {
+ public k4a_version_t rgb;
+ public k4a_version_t depth;
+ public k4a_version_t audio;
+ public k4a_version_t depth_sensor;
+ public FirmwareBuild firmware_build;
+ public FirmwareSignature firmware_signature;
+
+ public HardwareVersion ToHardwareVersion()
+ {
+ return new HardwareVersion
+ {
+ RGB = this.rgb.ToVersion(),
+ Depth = this.depth.ToVersion(),
+ Audio = this.audio.ToVersion(),
+ DepthSensor = this.depth_sensor.ToVersion(),
+ FirmwareBuild = this.firmware_build,
+ FirmwareSignature = this.firmware_signature,
+ };
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct k4a_device_configuration_t
+ {
+ public ImageFormat color_format;
+ public ColorResolution color_resolution;
+ public DepthMode depth_mode;
+ public FPS camera_fps;
+ public bool synchronized_images_only;
+ public int depth_delay_off_color_usec;
+ public WiredSyncMode wired_sync_mode;
+ public uint subordinate_delay_off_master_usec;
+ public bool disable_streaming_indicator;
+
+ public static k4a_device_configuration_t FromDeviceConfiguration(DeviceConfiguration configuration)
+ {
+ // Ticks are in 100ns units
+ int depth_delay_off_color_usec = checked((int)(
+ configuration.DepthDelayOffColor.Ticks / 10));
+
+ uint subordinate_delay_off_master_usec = checked((uint)(
+ configuration.SuboridinateDelayOffMaster.Ticks / 10));
+
+ return new NativeMethods.k4a_device_configuration_t
+ {
+ color_format = configuration.ColorFormat,
+ color_resolution = configuration.ColorResolution,
+ depth_mode = configuration.DepthMode,
+ camera_fps = configuration.CameraFPS,
+ synchronized_images_only = configuration.SynchronizedImagesOnly,
+ depth_delay_off_color_usec = depth_delay_off_color_usec,
+ wired_sync_mode = configuration.WiredSyncMode,
+ subordinate_delay_off_master_usec = subordinate_delay_off_master_usec,
+ disable_streaming_indicator = configuration.DisableStreamingIndicator,
+ };
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
+ public struct k4a_record_configuration_t
+ {
+ public ImageFormat color_format;
+ public ColorResolution color_resolution;
+ public DepthMode depth_mode;
+ public FPS camera_fps;
+ public byte color_track_enabled;
+ public byte depth_track_enabled;
+ public byte ir_track_enabled;
+ public byte imu_track_enabled;
+ public int depth_delay_off_color_usec;
+ public WiredSyncMode wired_sync_mode;
+ public uint subordinate_delay_off_master_usec;
+ public uint start_timestamp_offset_usec;
+ }
+
+ public class k4a_record_t : Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid
+ {
+ private k4a_record_t()
+ : base(true)
+ {
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ NativeMethods.k4a_record_close(this.handle);
+ return true;
+ }
+ }
+
+ public class k4a_playback_t : Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid
+ {
+ private k4a_playback_t()
+ : base(true)
+ {
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ NativeMethods.k4a_playback_close(this.handle);
+ return true;
+ }
+ }
+
+ public class k4a_playback_data_block_t : Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid
+ {
+ private k4a_playback_data_block_t()
+ : base(true)
+ {
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ NativeMethods.k4a_playback_data_block_release(this.handle);
+ return true;
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public class k4a_imu_sample_t
+ {
+ public float temperature { get; set; }
+
+ public Vector3 acc_sample { get; set; }
+
+ public ulong acc_timestamp_usec { get; set; }
+
+ public Vector3 gyro_sample { get; set; }
+
+ public ulong gyro_timestamp_usec { get; set; }
+
+ public ImuSample ToImuSample()
+ {
+ return new ImuSample
+ {
+ Temperature = this.temperature,
+ AccelerometerSample = this.acc_sample,
+ AccelerometerTimestamp = TimeSpan.FromTicks(checked((long)this.acc_timestamp_usec) * 10),
+ GyroSample = this.gyro_sample,
+ GyroTimestamp = TimeSpan.FromTicks(checked((long)this.gyro_timestamp_usec) * 10),
+ };
+ }
+ }
+ }
+#pragma warning restore CA2101 // Specify marshaling for P/Invoke string arguments
+#pragma warning restore SA1602 // Enumeration items should be documented
+#pragma warning restore SA1600 // Elements should be documented
+#pragma warning restore IDE1006 // Naming Styles
+}
diff --git a/src/csharp/Record/Playback.cs b/src/csharp/Record/Playback.cs
new file mode 100644
index 000000000..63dc07dfb
--- /dev/null
+++ b/src/csharp/Record/Playback.cs
@@ -0,0 +1,786 @@
+//------------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+//
+//------------------------------------------------------------------------------
+using System;
+using System.Text;
+using Microsoft.Azure.Kinect.Sensor.Record.Exceptions;
+
+namespace Microsoft.Azure.Kinect.Sensor.Record
+{
+ ///
+ /// Respresents a file being used to playback data from an Azure Kinect device.
+ ///
+ public class Playback : IDisposable
+ {
+ // The native handle for this recording.
+ private readonly NativeMethods.k4a_playback_t handle;
+
+ // To detect redundant calls to Dispose
+ private bool disposedValue = false;
+
+ // Cached values
+ private Calibration? calibration = null;
+ private RecordConfiguration recordConfiguration = null;
+
+ private Playback(NativeMethods.k4a_playback_t handle)
+ {
+ this.handle = handle;
+ }
+
+ ///
+ /// Gets get the camera calibration for Azure Kinect device used during recording. The output struct is used as input to all transformation functions.
+ ///
+ ///
+ /// The calibration may not exist if the device was not specified during recording.
+ ///
+ public Calibration? Calibration
+ {
+ get
+ {
+ lock (this)
+ {
+ if (this.disposedValue)
+ {
+ throw new ObjectDisposedException(nameof(Playback));
+ }
+
+ if (!this.calibration.HasValue)
+ {
+ if (NativeMethods.k4a_playback_get_calibration(this.handle, out Calibration localCalibration) == NativeMethods.k4a_result_t.K4A_RESULT_SUCCEEDED)
+ {
+ this.calibration = localCalibration;
+ }
+ }
+
+ return this.calibration;
+ }
+ }
+ }
+
+ ///
+ /// Gets get the device configuration used during recording.
+ ///
+ public RecordConfiguration RecordConfiguration
+ {
+ get
+ {
+ lock (this)
+ {
+ if (this.disposedValue)
+ {
+ throw new ObjectDisposedException(nameof(Playback));
+ }
+
+ if (this.recordConfiguration == null)
+ {
+ NativeMethods.k4a_record_configuration_t nativeConfig = new NativeMethods.k4a_record_configuration_t();
+
+ if (NativeMethods.k4a_playback_get_record_configuration(this.handle, ref nativeConfig) == NativeMethods.k4a_result_t.K4A_RESULT_SUCCEEDED)
+ {
+ this.recordConfiguration = RecordConfiguration.FromNative(nativeConfig);
+ }
+ }
+
+ return this.recordConfiguration;
+ }
+ }
+ }
+
+ ///
+ /// Gets get the number of tracks in a playback file.
+ ///
+ public int TrackCount
+ {
+ get
+ {
+ lock (this)
+ {
+ if (this.disposedValue)
+ {
+ throw new ObjectDisposedException(nameof(Playback));
+ }
+
+ return checked((int)NativeMethods.k4a_playback_get_track_count(this.handle));
+ }
+ }
+ }
+
+ ///
+ /// Gets the length of the recording in microseconds.
+ ///
+ ///
+ /// The recording length, calculated as the difference between the first and last timestamp in the file.
+ ///
+ /// The recording length may be longer than an individual track if, for example, the IMU continues to run after the last
+ /// color image is recorded.
+ ///
+ public TimeSpan RecordingLength
+ {
+ get
+ {
+ if (this.disposedValue)
+ {
+ throw new ObjectDisposedException(nameof(Playback));
+ }
+
+ long length = checked((long)NativeMethods.k4a_playback_get_recording_length_usec(this.handle));
+ return TimeSpan.FromTicks(length * 10);
+ }
+ }
+
+ ///
+ /// Opens an existing recording file for reading.
+ ///
+ /// Filesystem path of the existing recording.
+ /// An object representing the file for playback.
+ public static Playback Open(string path)
+ {
+ NativeMethods.k4a_playback_t handle = null;
+
+ AzureKinectOpenPlaybackException.ThrowIfNotSuccess(() => NativeMethods.k4a_playback_open(path, out handle));
+
+ return new Playback(handle);
+ }
+
+ ///
+ /// Get the raw calibration blob for the Azure Kinect device used during recording.
+ ///
+ /// The raw calibration may not exist if the device was not specified during recording.
+ public byte[] GetRawCalibration()
+ {
+ lock (this)
+ {
+ if (this.disposedValue)
+ {
+ throw new ObjectDisposedException(nameof(Playback));
+ }
+
+ // Determine the required calibration size
+ UIntPtr size = new UIntPtr(0);
+ if (NativeMethods.k4a_playback_get_raw_calibration(this.handle, null, ref size) != NativeMethods.k4a_buffer_result_t.K4A_BUFFER_RESULT_TOO_SMALL)
+ {
+ throw new AzureKinectGetRawCalibrationException($"Unexpected result calling {nameof(NativeMethods.k4a_playback_get_raw_calibration)}");
+ }
+
+ // Allocate a string buffer
+ byte[] raw = new byte[size.ToUInt32()];
+
+ // Get the raw calibration
+ AzureKinectGetRawCalibrationException.ThrowIfNotSuccess(() => NativeMethods.k4a_playback_get_raw_calibration(this.handle, raw, ref size));
+
+ return raw;
+ }
+ }
+
+ ///
+ /// Checks whether a track with the given track name exists in the playback file.
+ ///
+ /// The track name to be checked to see whether it exists or not.
+ /// True if the track exists in the file.
+ public bool CheckTrackExists(string trackName)
+ {
+ lock (this)
+ {
+ if (this.disposedValue)
+ {
+ throw new ObjectDisposedException(nameof(Playback));
+ }
+
+ if (trackName == null)
+ {
+ throw new ArgumentNullException(nameof(trackName));
+ }
+
+ return NativeMethods.k4a_playback_check_track_exists(this.handle, trackName);
+ }
+ }
+
+ ///
+ /// Gets the name of a track at a specific index.
+ ///
+ /// The index of the track to read the name form.
+ /// The name of the track.
+ /// When used along with , this function can be used to enumerate all the available tracks
+ /// in a playback file. Additionally can be used to filter custom tracks.
+ public string GetTrackName(int index)
+ {
+ lock (this)
+ {
+ if (this.disposedValue)
+ {
+ throw new ObjectDisposedException(nameof(Playback));
+ }
+
+ if (index >= this.TrackCount || index < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+
+ // Determine the required string size
+ UIntPtr size = new UIntPtr(0);
+ if (NativeMethods.k4a_playback_get_track_name(this.handle, (UIntPtr)index, null, ref size) != NativeMethods.k4a_buffer_result_t.K4A_BUFFER_RESULT_TOO_SMALL)
+ {
+ throw new AzureKinectException($"Unexpected internal state calling {nameof(NativeMethods.k4a_playback_get_track_name)}");
+ }
+
+ // Allocate a string buffer
+ StringBuilder trackname = new StringBuilder((int)size.ToUInt32());
+
+ // Get the track name
+ AzureKinectGetTrackNameException.ThrowIfNotSuccess(() => NativeMethods.k4a_playback_get_track_name(this.handle, (UIntPtr)index, trackname, ref size));
+
+ return trackname.ToString();
+ }
+ }
+
+ ///
+ /// Checks whether a track is one of the built-in tracks: "COLOR", "DEPTH", etc...
+ ///
+ /// The track name to be checked to see whether it is a built-in track.
+ /// true if the track is built-in. If the provided track name does not exist, false will be returned.
+ public bool GetTrackIsBuiltin(string trackName)
+ {
+ lock (this)
+ {
+ if (this.disposedValue)
+ {
+ throw new ObjectDisposedException(nameof(Playback));
+ }
+
+ if (trackName == null)
+ {
+ throw new ArgumentNullException(nameof(trackName));
+ }
+
+ return NativeMethods.k4a_playback_track_is_builtin(this.handle, trackName);
+ }
+ }
+
+ ///
+ /// Gets the video-specific track information for a particular video track.
+ ///
+ /// The track name to read video settings from.
+ /// The track's video settings.
+ public RecordVideoSettings GetTrackVideoSettings(string trackName)
+ {
+ lock (this)
+ {
+ if (this.disposedValue)
+ {
+ throw new ObjectDisposedException(nameof(Playback));
+ }
+
+ if (trackName == null)
+ {
+ throw new ArgumentNullException(nameof(trackName));
+ }
+
+ RecordVideoSettings recordVideoSettings = new RecordVideoSettings();
+
+ AzureKinectTrackGetVideoSettingsException.ThrowIfNotSuccess(() => NativeMethods.k4a_playback_track_get_video_settings(this.handle, trackName, out recordVideoSettings));
+
+ return recordVideoSettings;
+ }
+ }
+
+ ///
+ /// Gets the codec id string for a particular track.
+ ///
+ /// The track name to read the codec id from.
+ /// Codec ID for the track.
+ ///
+ /// The codec ID is a string that corresponds to the codec of the track's data. Some of the existing formats are listed
+ /// here: https://www.matroska.org/technical/specs/codecid/index.html. It can also be custom defined by the user.
+ ///
+ public string GetTrackCodecId(string trackName)
+ {
+ lock (this)
+ {
+ if (this.disposedValue)
+ {
+ throw new ObjectDisposedException(nameof(Playback));
+ }
+
+ if (trackName == null)
+ {
+ throw new ArgumentNullException(nameof(trackName));
+ }
+
+ // Determine the required string size
+ UIntPtr size = new UIntPtr(0);
+ if (NativeMethods.k4a_playback_track_get_codec_id(this.handle, trackName, null, ref size) != NativeMethods.k4a_buffer_result_t.K4A_BUFFER_RESULT_TOO_SMALL)
+ {
+ throw new AzureKinectException($"Unexpected internal state calling {nameof(NativeMethods.k4a_playback_get_track_name)}");
+ }
+
+ // Allocate a string buffer
+ StringBuilder codec = new StringBuilder((int)size.ToUInt32());
+
+ // Get the codec id
+ AzureKinectGetTrackNameException.ThrowIfNotSuccess(() => NativeMethods.k4a_playback_track_get_codec_id(this.handle, trackName, codec, ref size));
+
+ return codec.ToString();
+ }
+ }
+
+ ///
+ /// Gets the codec context for a particular track.
+ ///
+ /// The track name to read the codec context from.
+ /// The codec context data.
+ ///
+ /// The codec context is a codec-specific buffer that contains any required codec metadata that is only known to the
+ /// codec. It is mapped to the matroska Codec Private field.
+ ///
+ public byte[] GetTrackCodecContext(string trackName)
+ {
+ lock (this)
+ {
+ if (this.disposedValue)
+ {
+ throw new ObjectDisposedException(nameof(Playback));
+ }
+
+ if (trackName == null)
+ {
+ throw new ArgumentNullException(nameof(trackName));
+ }
+
+ // Determine the required buffer size
+ UIntPtr size = new UIntPtr(0);
+ if (NativeMethods.k4a_playback_track_get_codec_context(this.handle, trackName, null, ref size) != NativeMethods.k4a_buffer_result_t.K4A_BUFFER_RESULT_TOO_SMALL)
+ {
+ throw new AzureKinectException($"Unexpected internal state calling {nameof(NativeMethods.k4a_playback_get_track_name)}");
+ }
+
+ // Allocate a buffer
+ byte[] context = new byte[checked((int)size)];
+
+ // Get the codec id
+ AzureKinectGetTrackNameException.ThrowIfNotSuccess(() => NativeMethods.k4a_playback_track_get_codec_context(this.handle, trackName, context, ref size));
+
+ return context;
+ }
+ }
+
+ ///
+ /// Read the value of a tag from a recording.
+ ///
+ /// The name of the tag to read.
+ /// The value of the tag.
+ public string GetTag(string name)
+ {
+ lock (this)
+ {
+ if (this.disposedValue)
+ {
+ throw new ObjectDisposedException(nameof(Playback));
+ }
+
+ if (name == null)
+ {
+ throw new ArgumentNullException(nameof(name));
+ }
+
+ // Determine the required string size
+ UIntPtr size = new UIntPtr(0);
+ NativeMethods.k4a_buffer_result_t result;
+
+#pragma warning disable CA1508 // Avoid dead conditional code
+ using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider))
+#pragma warning restore CA1508 // Avoid dead conditional code
+ {
+ result = NativeMethods.k4a_playback_get_tag(this.handle, name, null, ref size);
+
+ if (result == NativeMethods.k4a_buffer_result_t.K4A_BUFFER_RESULT_FAILED)
+ {
+ AzureKinectGetTagException.ThrowIfNotSuccess(tracer, result);
+ }
+ }
+
+ if (result != NativeMethods.k4a_buffer_result_t.K4A_BUFFER_RESULT_TOO_SMALL)
+ {
+ throw new AzureKinectException($"Unexpected internal state calling {nameof(NativeMethods.k4a_playback_get_track_name)}");
+ }
+
+ // Allocate a string buffer
+ StringBuilder value = new StringBuilder((int)size.ToUInt32());
+
+ // Get the codec id
+ AzureKinectGetTagException.ThrowIfNotSuccess(() => NativeMethods.k4a_playback_get_tag(this.handle, name, value, ref size));
+
+ return value.ToString();
+ }
+ }
+
+ ///
+ /// 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.
+ ///
+ /// The target image format to be returned in captures.
+ ///
+ /// Color format conversion occurs in the user-thread, so setting to anything other than the format
+ /// stored in the file may significantly increase the latency of and .
+ ///
+ public void SetColorConversion(ImageFormat targetFormat)
+ {
+ lock (this)
+ {
+ if (this.disposedValue)
+ {
+ throw new ObjectDisposedException(nameof(Playback));
+ }
+
+ AzureKinectSetColorConversionException.ThrowIfNotSuccess(() => NativeMethods.k4a_playback_set_color_conversion(this.handle, targetFormat));
+ }
+ }
+
+ ///
+ /// Reads an attachment file from a recording.
+ ///
+ /// The attachment file name.
+ /// The attachment data.
+ public byte[] GetAttachment(string fileName)
+ {
+ lock (this)
+ {
+ if (this.disposedValue)
+ {
+ throw new ObjectDisposedException(nameof(Playback));
+ }
+
+ if (fileName == null)
+ {
+ throw new ArgumentNullException(nameof(fileName));
+ }
+
+ // Determine the required buffer size
+ UIntPtr size = new UIntPtr(0);
+ if (NativeMethods.k4a_playback_get_attachment(this.handle, fileName, null, ref size) != NativeMethods.k4a_buffer_result_t.K4A_BUFFER_RESULT_TOO_SMALL)
+ {
+ throw new AzureKinectException($"Unexpected internal state calling {nameof(NativeMethods.k4a_playback_get_track_name)}");
+ }
+
+ // Allocate a buffer
+ byte[] buffer = new byte[checked((int)size)];
+
+ // Get the codec id
+ AzureKinectGetTrackNameException.ThrowIfNotSuccess(() => NativeMethods.k4a_playback_get_attachment(this.handle, fileName, buffer, ref size));
+
+ return buffer;
+ }
+ }
+
+ ///
+ /// Read the next capture in the recording sequence.
+ ///
+ /// The next capture in the sequence, or null if at the end of the sequence.
+ ///
+ /// always returns the next capture in sequence after the most recently returned capture.
+ ///
+ /// The first call to after will return the capture
+ /// in the recording closest to the seek time with an image timestamp greater than or equal to the seek time.
+ ///
+ /// If a call was made to that returned null, the playback
+ /// position is at the beginning of the stream and will return the first capture in the
+ /// recording.
+ ///
+ /// Capture objects returned by the playback API will always contain at least one image, but may have images missing if
+ /// frames were dropped in the original recording. When calling ,
+ /// , or , the image should be checked for null.
+ ///
+ public Capture GetNextCapture()
+ {
+ lock (this)
+ {
+ if (this.disposedValue)
+ {
+ throw new ObjectDisposedException(nameof(Playback));
+ }
+
+ IntPtr captureHandle = IntPtr.Zero;
+
+ switch (NativeMethods.k4a_playback_get_next_capture(this.handle, out captureHandle))
+ {
+ case NativeMethods.k4a_stream_result_t.K4A_STREAM_RESULT_EOF:
+ return null;
+ case NativeMethods.k4a_stream_result_t.K4A_STREAM_RESULT_FAILED:
+ throw new AzureKinectGetCaptureException();
+ case NativeMethods.k4a_stream_result_t.K4A_STREAM_RESULT_SUCCEEDED:
+ return new Capture(true, captureHandle);
+ }
+
+ return null;
+ }
+ }
+
+ ///
+ /// Read the previous capture in the recording sequence.
+ ///
+ /// The previous capture in the sequence, or null if at the beginning of the sequence.
+ ///
+ /// always returns the previous capture in sequence after the most recently returned capture.
+ ///
+ /// The first call to after will return the capture
+ /// in the recording closest to the seek time with all image timestamps less than the seek time.
+ ///
+ /// If a call was made to that returned null, the playback
+ /// position is at the end of the stream and will return the last capture in the
+ /// recording.
+ ///
+ /// Capture objects returned by the playback API will always contain at least one image, but may have images missing if
+ /// frames were dropped in the original recording. When calling ,
+ /// , or , the image should be checked for null.
+ ///
+ public Capture GetPreviousCapture()
+ {
+ lock (this)
+ {
+ if (this.disposedValue)
+ {
+ throw new ObjectDisposedException(nameof(Playback));
+ }
+
+ IntPtr captureHandle = IntPtr.Zero;
+
+ switch (NativeMethods.k4a_playback_get_previous_capture(this.handle, out captureHandle))
+ {
+ case NativeMethods.k4a_stream_result_t.K4A_STREAM_RESULT_EOF:
+ return null;
+ case NativeMethods.k4a_stream_result_t.K4A_STREAM_RESULT_FAILED:
+ throw new AzureKinectGetCaptureException();
+ case NativeMethods.k4a_stream_result_t.K4A_STREAM_RESULT_SUCCEEDED:
+ return new Capture(true, captureHandle);
+ }
+
+ return null;
+ }
+ }
+
+ ///
+ /// Read the next IMU sample in the recording sequence.
+ ///
+ /// The next IMU sample in the sequence, or null if at the end of the sequence.
+ ///
+ /// always returns the next IMU sample in sequence after the most recently returned sample.
+ ///
+ /// The first call to after will return the sample
+ /// in the recording closest to the seek time with a timestamp greater than or equal to the seek time.
+ ///
+ /// If a call was made to that returned null, the playback
+ /// position is at the beginning of the stream and will return the first sample in the
+ /// recording.
+ ///
+ public ImuSample GetNextImuSample()
+ {
+ lock (this)
+ {
+ if (this.disposedValue)
+ {
+ throw new ObjectDisposedException(nameof(Playback));
+ }
+
+ NativeMethods.k4a_imu_sample_t imu_sample = new NativeMethods.k4a_imu_sample_t();
+
+ switch (NativeMethods.k4a_playback_get_next_imu_sample(this.handle, imu_sample))
+ {
+ case NativeMethods.k4a_stream_result_t.K4A_STREAM_RESULT_EOF:
+ return null;
+ case NativeMethods.k4a_stream_result_t.K4A_STREAM_RESULT_FAILED:
+ throw new AzureKinectGetImuSampleException();
+ case NativeMethods.k4a_stream_result_t.K4A_STREAM_RESULT_SUCCEEDED:
+ return imu_sample.ToImuSample();
+ }
+
+ return null;
+ }
+ }
+
+ ///
+ /// Read the previous IMU sample in the recording sequence.
+ ///
+ /// The previous IMU sample in the sequence, or null if at the beginning of the sequence.
+ ///
+ /// always returns the previous IMU sample in sequence before the most recently returned sample.
+ ///
+ /// The first call to after will return the sample
+ /// in the recording closest to the seek time with a timestamp less than the seek time.
+ ///
+ /// If a call was made to that returned null, the playback
+ /// position is at the end of the stream and will return the last sample in the
+ /// recording.
+ ///
+ public ImuSample GetPreviousImuSample()
+ {
+ lock (this)
+ {
+ if (this.disposedValue)
+ {
+ throw new ObjectDisposedException(nameof(Playback));
+ }
+
+ NativeMethods.k4a_imu_sample_t imu_sample = new NativeMethods.k4a_imu_sample_t();
+
+ switch (NativeMethods.k4a_playback_get_previous_imu_sample(this.handle, imu_sample))
+ {
+ case NativeMethods.k4a_stream_result_t.K4A_STREAM_RESULT_EOF:
+ return null;
+ case NativeMethods.k4a_stream_result_t.K4A_STREAM_RESULT_FAILED:
+ throw new AzureKinectGetImuSampleException();
+ case NativeMethods.k4a_stream_result_t.K4A_STREAM_RESULT_SUCCEEDED:
+ return imu_sample.ToImuSample();
+ }
+
+ return null;
+ }
+ }
+
+ ///
+ /// Read the next data block for a particular track.
+ ///
+ /// The name of the track to read the next data block from.
+ /// The next data block in the sequence, or null if at the end of the sequence.
+ ///
+ /// always returns the next data block in sequence after the most recently returned data block
+ /// for a particular track.
+ ///
+ /// The first call to after will return the data block
+ /// in the recording closest to the seek time with a timestamp greater than or equal to the seek time.
+ ///
+ /// If a call was made to that returned null for a particular track, the playback
+ /// position is at the beginning of the stream and will return the first data block in the
+ /// recording.
+ ///
+ public DataBlock GetNextDataBlock(string trackName)
+ {
+ lock (this)
+ {
+ if (this.disposedValue)
+ {
+ throw new ObjectDisposedException(nameof(Playback));
+ }
+
+ if (trackName == null)
+ {
+ throw new ArgumentNullException(nameof(trackName));
+ }
+
+ switch (NativeMethods.k4a_playback_get_next_data_block(this.handle, trackName, out NativeMethods.k4a_playback_data_block_t dataBlock))
+ {
+ case NativeMethods.k4a_stream_result_t.K4A_STREAM_RESULT_EOF:
+ return null;
+ case NativeMethods.k4a_stream_result_t.K4A_STREAM_RESULT_FAILED:
+ throw new AzureKinectGetDataBlockException();
+ case NativeMethods.k4a_stream_result_t.K4A_STREAM_RESULT_SUCCEEDED:
+ return new DataBlock(dataBlock);
+ }
+
+ return null;
+ }
+ }
+
+ ///
+ /// Read the previous data block for a particular track.
+ ///
+ /// The name of the track to read the previous data block from.
+ /// The previous data block in the sequence, or null if at the beginning of the sequence.
+ ///
+ /// always returns the previous data block in sequence after the most recently returned data block
+ /// for a particular track.
+ ///
+ /// The first call to after will return the data block
+ /// in the recording closest to the seek time with a timestamp less than the seek time.
+ ///
+ /// If a call was made to that returned null for a particular track, the playback
+ /// position is at the end of the stream and will return the last data block in the
+ /// recording.
+ ///
+ public DataBlock GetPreviousDataBlock(string trackName)
+ {
+ lock (this)
+ {
+ if (this.disposedValue)
+ {
+ throw new ObjectDisposedException(nameof(Playback));
+ }
+
+ if (trackName == null)
+ {
+ throw new ArgumentNullException(nameof(trackName));
+ }
+
+ switch (NativeMethods.k4a_playback_get_previous_data_block(this.handle, trackName, out NativeMethods.k4a_playback_data_block_t dataBlock))
+ {
+ case NativeMethods.k4a_stream_result_t.K4A_STREAM_RESULT_EOF:
+ return null;
+ case NativeMethods.k4a_stream_result_t.K4A_STREAM_RESULT_FAILED:
+ throw new AzureKinectGetDataBlockException();
+ case NativeMethods.k4a_stream_result_t.K4A_STREAM_RESULT_SUCCEEDED:
+ return new DataBlock(dataBlock);
+ }
+
+ return null;
+ }
+ }
+
+ ///
+ /// Seek to a specific timestamp within a recording.
+ ///
+ /// The timestamp offset to seek to, relative to .
+ /// Specifies how the given timestamp should be interpreted. Seek can be done relative to the beginning or end of the
+ /// recording, or using an absolute device timestamp.
+ ///
+ /// The first device timestamp in a recording is usually non-zero. The recording file starts at the device timestamp
+ /// defined by , which is accessible via .
+ ///
+ /// The first call to after will return a capture containing an image
+ /// timestamp greater than or equal to the seek time.
+ ///
+ /// The first call to after will return a capture with
+ /// all image timstamps less than the seek time.
+ ///
+ /// The first call to and after will return the
+ /// first data with a timestamp greater than or equal to the seek time.
+ ///
+ /// The first call to and after will return the
+ /// first data with a timestamp less than the seek time.
+ ///
+ public void Seek(TimeSpan offset, PlaybackSeekOrigin origin = PlaybackSeekOrigin.Begin)
+ {
+ lock (this)
+ {
+ if (this.disposedValue)
+ {
+ throw new ObjectDisposedException(nameof(Playback));
+ }
+
+ AzureKinectSeekException.ThrowIfNotSuccess(() => NativeMethods.k4a_playback_seek_timestamp(this.handle, checked((ulong)(offset.Ticks / 10)), origin));
+ }
+ }
+
+ ///
+ public void Dispose()
+ {
+ this.Dispose(true);
+
+ GC.SuppressFinalize(this);
+ }
+
+ ///
+ /// Handle the disposing of the object.
+ ///
+ /// true when called by Dispose(), false when called by the finalizer.
+ protected virtual void Dispose(bool disposing)
+ {
+ lock (this)
+ {
+ this.handle.Close();
+
+ this.disposedValue = true;
+ }
+ }
+ }
+}
diff --git a/src/csharp/Record/PlaybackSeekOrigin.cs b/src/csharp/Record/PlaybackSeekOrigin.cs
new file mode 100644
index 000000000..1f8caf5cf
--- /dev/null
+++ b/src/csharp/Record/PlaybackSeekOrigin.cs
@@ -0,0 +1,29 @@
+//------------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+//
+//------------------------------------------------------------------------------
+namespace Microsoft.Azure.Kinect.Sensor.Record
+{
+ ///
+ /// The origin for relative seek operations.
+ ///
+ public enum PlaybackSeekOrigin
+ {
+ ///
+ /// The seek operation is relative to the beginning of the file.
+ ///
+ Begin = 0,
+
+ ///
+ /// The seek operation is relative to the end of the file.
+ ///
+ End,
+
+ ///
+ /// The seek operation is specified in the device time.
+ ///
+ DeviceTime,
+ }
+}
diff --git a/src/csharp/Record/RecordConfiguration.cs b/src/csharp/Record/RecordConfiguration.cs
new file mode 100644
index 000000000..d77283b03
--- /dev/null
+++ b/src/csharp/Record/RecordConfiguration.cs
@@ -0,0 +1,110 @@
+//------------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+//
+//------------------------------------------------------------------------------
+using System;
+
+namespace Microsoft.Azure.Kinect.Sensor.Record
+{
+ ///
+ /// Structure containing the device configuration used to record.
+ ///
+ public class RecordConfiguration
+ {
+ ///
+ /// Gets or sets the image format used to record the color camera.
+ ///
+ public ImageFormat ColorFormat { get; set; }
+
+ ///
+ /// Gets or sets the image resolution used to record the color camera.
+ ///
+ public ColorResolution ColorResolution { get; set; }
+
+ ///
+ /// Gets or sets the mode used to record the depth camera.
+ ///
+ public DepthMode DepthMode { get; set; }
+
+ ///
+ /// Gets or sets the frame rate used to record the color and depth camera.
+ ///
+ public FPS CameraFPS { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether the recording contains Color camera frames.
+ ///
+ public bool ColorTrackEnabled { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether the recording contains Depth camera frames.
+ ///
+ public bool DepthTrackEnabled { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether the recording contains IR camera frames.
+ ///
+ public bool IRTrackEnabled { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether the recording contains IMU sample data.
+ ///
+ public bool ImuTrackEnabled { get; set; }
+
+ ///
+ /// Gets or sets the delay between color and depth images in the recording.
+ ///
+ ///
+ /// A negative delay means depth images are first, and a positive delay means color images are first.
+ ///
+ public TimeSpan DepthDelayOffColor { get; set; }
+
+ ///
+ /// Gets or sets external synchronization mode.
+ ///
+ public WiredSyncMode WiredSyncMode { get; set; }
+
+ ///
+ /// Gets or sets the delay between this recording and the externally synced master camera.
+ ///
+ ///
+ /// This value is 0 unless is set to .
+ ///
+ public TimeSpan SubordinateDelayOffMaster { get; set; }
+
+ ///
+ /// Gets or sets the timestamp offset of the start of the recording.
+ ///
+ ///
+ /// All recorded timestamps are offset by this value such that
+ /// the recording starts at timestamp 0. This value can be used to synchronize timestamps between 2 recording files.
+ ///
+ public TimeSpan StartTimestampOffset { get; set; }
+
+ ///
+ /// Gets a object from a native object.
+ ///
+ /// Native object.
+ /// Managed object.
+ internal static RecordConfiguration FromNative(NativeMethods.k4a_record_configuration_t config)
+ {
+ return new RecordConfiguration()
+ {
+ ColorFormat = config.color_format,
+ ColorResolution = config.color_resolution,
+ DepthMode = config.depth_mode,
+ CameraFPS = config.camera_fps,
+ ColorTrackEnabled = config.color_track_enabled == 0 ? false : true,
+ DepthTrackEnabled = config.depth_track_enabled == 0 ? false : true,
+ IRTrackEnabled = config.ir_track_enabled == 0 ? false : true,
+ ImuTrackEnabled = config.imu_track_enabled == 0 ? false : true,
+ DepthDelayOffColor = TimeSpan.FromTicks(config.subordinate_delay_off_master_usec * 10),
+ WiredSyncMode = config.wired_sync_mode,
+ SubordinateDelayOffMaster = TimeSpan.FromTicks(config.subordinate_delay_off_master_usec * 10),
+ StartTimestampOffset = TimeSpan.FromTicks(config.start_timestamp_offset_usec),
+ };
+ }
+ }
+}
diff --git a/src/csharp/Record/RecordLogger.cs b/src/csharp/Record/RecordLogger.cs
new file mode 100644
index 000000000..18eb11ac8
--- /dev/null
+++ b/src/csharp/Record/RecordLogger.cs
@@ -0,0 +1,147 @@
+//------------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+//
+//------------------------------------------------------------------------------
+using System;
+using System.Diagnostics;
+
+namespace Microsoft.Azure.Kinect.Sensor.Record
+{
+ ///
+ /// The Azure Kinect logging system. Enables access to the debug messages from the the Azure Kinect Record and Playback API.
+ ///
+ public static class RecordLogger
+ {
+ private static readonly object SyncRoot = new object();
+ private static readonly NativeMethods.k4a_logging_message_cb_t DebugMessageHandler = OnDebugMessage;
+ private static readonly RecordLoggerProvider LoggerProvider = new RecordLoggerProvider();
+ private static bool isInitialized;
+
+#pragma warning disable CA1003 // Use generic event handler instances
+ ///
+ /// Occurs when the Azure Kinect Sensor Record and Playback SDK delivers a debug message.
+ ///
+ public static event Action LogMessage
+#pragma warning restore CA1003 // Use generic event handler instances
+ {
+ add
+ {
+ lock (SyncRoot)
+ {
+ if (!RecordLogger.isInitialized)
+ {
+ RecordLogger.Initialize();
+ }
+
+ LogMessageHandlers += value;
+ }
+ }
+
+ remove
+ {
+ lock (SyncRoot)
+ {
+ LogMessageHandlers -= value;
+ }
+ }
+ }
+
+ private static event Action LogMessageHandlers;
+
+ ///
+ /// Gets the interface for reading log messages.
+ ///
+ public static ILoggingProvider LogProvider => RecordLogger.LoggerProvider;
+
+ ///
+ /// Initializes the class to begin receiving messages from the Azure Kinect Sensor SDK.
+ ///
+ public static void Initialize()
+ {
+ lock (SyncRoot)
+ {
+ if (RecordLogger.isInitialized)
+ {
+ return;
+ }
+
+ AppDomain.CurrentDomain.ProcessExit += CurrentDomain_Exit;
+ AppDomain.CurrentDomain.DomainUnload += CurrentDomain_Exit;
+ NativeMethods.k4a_result_t result = NativeMethods.k4a_record_set_debug_message_handler(DebugMessageHandler, IntPtr.Zero, LogLevel.Trace);
+ if (result != NativeMethods.k4a_result_t.K4A_RESULT_SUCCEEDED)
+ {
+ throw new AzureKinectException("Failed to set the Debug Message Handler");
+ }
+
+ RecordLogger.isInitialized = true;
+ }
+ }
+
+ ///
+ /// Resets the logger to an uninitialized state. This is used in the Unit Tests to ensure that the
+ /// initialization is run during the unit tests.
+ ///
+ internal static void Reset()
+ {
+ lock (SyncRoot)
+ {
+ if (!RecordLogger.isInitialized)
+ {
+ return;
+ }
+
+ AppDomain.CurrentDomain.ProcessExit -= CurrentDomain_Exit;
+ AppDomain.CurrentDomain.DomainUnload -= CurrentDomain_Exit;
+
+ // TODO: This won't work as Raise Error has an invalid pointer.
+ ////NativeMethods.k4a_result_t result = NativeMethods.k4a_set_debug_message_handler(null, IntPtr.Zero, LogLevel.Trace);
+ ////if (result != NativeMethods.k4a_result_t.K4A_RESULT_SUCCEEDED)
+ ////{
+ //// throw new AzureKinectException("Failed to set the Debug Message Handler");
+ ////}
+
+ RecordLogger.isInitialized = false;
+ }
+ }
+
+ private static void OnDebugMessage(IntPtr context, LogLevel level, string file, int line, string message)
+ {
+ LogMessage data = new LogMessage(DateTime.Now, level, file, line, message);
+
+ Action eventhandler = LogMessageHandlers;
+ if (eventhandler != null)
+ {
+ eventhandler(data);
+ }
+ }
+
+ private static void CurrentDomain_Exit(object sender, EventArgs e)
+ {
+ NativeMethods.k4a_result_t result = NativeMethods.k4a_record_set_debug_message_handler(null, IntPtr.Zero, LogLevel.Off);
+ if (result != NativeMethods.k4a_result_t.K4A_RESULT_SUCCEEDED)
+ {
+ Trace.WriteLine("Failed to close the debug message handler");
+ }
+ }
+
+ private class RecordLoggerProvider : ILoggingProvider
+ {
+ public event Action LogMessage
+ {
+ add
+ {
+ RecordLogger.LogMessage += value;
+ }
+
+ remove
+ {
+ RecordLogger.LogMessage -= value;
+ }
+ }
+
+ public string ProviderName => "Azure Kinect Recording SDK";
+ }
+ }
+}
diff --git a/src/csharp/Record/RecordSubtitleSettings.cs b/src/csharp/Record/RecordSubtitleSettings.cs
new file mode 100644
index 000000000..d7b666b37
--- /dev/null
+++ b/src/csharp/Record/RecordSubtitleSettings.cs
@@ -0,0 +1,26 @@
+//------------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+//
+//------------------------------------------------------------------------------
+using System;
+using System.Runtime.InteropServices;
+
+namespace Microsoft.Azure.Kinect.Sensor.Record
+{
+ ///
+ /// Settings for a recording subtitle track.
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ public class RecordSubtitleSettings
+ {
+ ///
+ /// Gets or sets a value indicating whether data will be grouped together to reduce overhead.
+ ///
+ ///
+ /// If set, only a single timestamp will be stored per batch, and an estimated timestamp will be use by and .
+ /// The estimated timestamp is calculated with the assumption that blocks are evenly spaced within a batch.
+ public bool HighFrequencyData { get; set; }
+ }
+}
diff --git a/src/csharp/Record/RecordVideoSettings.cs b/src/csharp/Record/RecordVideoSettings.cs
new file mode 100644
index 000000000..bcdd1cc95
--- /dev/null
+++ b/src/csharp/Record/RecordVideoSettings.cs
@@ -0,0 +1,32 @@
+//------------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+//
+//------------------------------------------------------------------------------
+using System.Runtime.InteropServices;
+
+namespace Microsoft.Azure.Kinect.Sensor.Record
+{
+ ///
+ /// Structure containing additional metadata specific to custom video tracks.
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ public class RecordVideoSettings
+ {
+ ///
+ /// Gets or sets frame width of the video.
+ ///
+ public long Width { get; set; }
+
+ ///
+ /// Gets or sets frame height of the video.
+ ///
+ public long Height { get; set; }
+
+ ///
+ /// Gets or sets frame rate of the video.
+ ///
+ public long FrameRate { get; set; }
+ }
+}
diff --git a/src/csharp/Record/Recorder.cs b/src/csharp/Record/Recorder.cs
new file mode 100644
index 000000000..c5b694272
--- /dev/null
+++ b/src/csharp/Record/Recorder.cs
@@ -0,0 +1,365 @@
+//------------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+//
+//------------------------------------------------------------------------------
+using System;
+using Microsoft.Azure.Kinect.Sensor.Record.Exceptions;
+
+namespace Microsoft.Azure.Kinect.Sensor.Record
+{
+ ///
+ /// Represents a writable sensor recording.
+ ///
+ public class Recorder : IDisposable
+ {
+ // The native handle for this recording.
+ private readonly NativeMethods.k4a_record_t handle;
+
+ // To detect redundant calls to Dispose
+ private bool disposedValue = false;
+
+ private Recorder(NativeMethods.k4a_record_t handle)
+ {
+ this.handle = handle;
+ }
+
+ ///
+ /// Create a recording.
+ ///
+ /// Path to the recording.
+ /// Device to get properties from. May be null for user-generated recordings.
+ /// Parameters used to open the device.
+ /// A new recording object.
+ public static Recorder Create(string path, Device device, DeviceConfiguration deviceConfiguration)
+ {
+ if (path == null)
+ {
+ throw new ArgumentNullException(nameof(path));
+ }
+
+ if (deviceConfiguration == null)
+ {
+ throw new ArgumentNullException(nameof(deviceConfiguration));
+ }
+
+ NativeMethods.k4a_record_t handle = null;
+ NativeMethods.k4a_device_configuration_t configuration = NativeMethods.k4a_device_configuration_t.FromDeviceConfiguration(deviceConfiguration);
+ if (device != null)
+ {
+ // If a device was specified, lock that device to avoid disposal while in use.
+ // Device.Dispose will take the same lock.
+ lock (device)
+ {
+ AzureKinectCreateRecordingException.ThrowIfNotSuccess(path, () => NativeMethods.k4a_record_create(path, device.Handle, configuration, out handle));
+ }
+ }
+ else
+ {
+ AzureKinectCreateRecordingException.ThrowIfNotSuccess(path, () => NativeMethods.k4a_record_create(path, IntPtr.Zero, configuration, out handle));
+ }
+
+ return new Recorder(handle);
+ }
+
+ ///
+ /// Adds a tag to the recroding.
+ ///
+ /// Name of the tag.
+ /// Value of the tag.
+ public void AddTag(string name, string value)
+ {
+ lock (this)
+ {
+ if (this.disposedValue)
+ {
+ throw new ObjectDisposedException(nameof(Recorder));
+ }
+
+ AzureKinectAddTagException.ThrowIfNotSuccess(() => NativeMethods.k4a_record_add_tag(this.handle, name, value));
+ }
+ }
+
+ ///
+ /// Adds an IMU track to the recording.
+ ///
+ public void AddImuTrack()
+ {
+ lock (this)
+ {
+ if (this.disposedValue)
+ {
+ throw new ObjectDisposedException(nameof(Recorder));
+ }
+
+ AzureKinectAddImuTrackException.ThrowIfNotSuccess(() => NativeMethods.k4a_record_add_imu_track(this.handle));
+ }
+ }
+
+ ///
+ /// Adds an attachment to a recording.
+ ///
+ /// The name of the attachment to be stored in the recording file. This name should be a valid filename with an extension.
+ /// The attachment data buffer.
+ public void AddAttachment(string attachmentName, byte[] buffer)
+ {
+ lock (this)
+ {
+ if (this.disposedValue)
+ {
+ throw new ObjectDisposedException(nameof(Recorder));
+ }
+
+ AzureKinectAddAttachmentException.ThrowIfNotSuccess(() => NativeMethods.k4a_record_add_attachment(this.handle, attachmentName, buffer, (UIntPtr)buffer.Length));
+ }
+ }
+
+ ///
+ /// Adds custom video tracks to the recording.
+ ///
+ /// The name of the custom video track to be added.
+ /// A UTF8 null terminated string containing the codec ID of the track.
+ /// Some of the existing formats are listed here: https://www.matroska.org/technical/specs/codecid/index.html.
+ /// The codec ID can also be custom defined by the user. Video codec ID's should start with 'V_'.
+ /// The codec context is a codec-specific buffer that contains any required codec metadata that is only known to the codec. It is mapped to the matroska 'CodecPrivate' element.
+ /// Additional metadata for the video track such as resolution and framerate.
+ ///
+ /// Built-in video tracks like the DEPTH, IR, and COLOR tracks will be created automatically when the k4a_record_create()
+ /// API is called.This API can be used to add additional video tracks to save custom data.
+ ///
+ /// Track names must be ALL CAPS and may only contain A-Z, 0-9, '-' and '_'.
+ ///
+ /// All tracks need to be added before the recording header is written.
+ ///
+ /// Call k4a_record_write_custom_track_data() with the same track_name to write data to this track.
+ ///
+ ///
+ public void AddCustomVideoTrack(
+ string trackName,
+ string codecId,
+ byte[] codecContext,
+ RecordVideoSettings trackSettings)
+ {
+ lock (this)
+ {
+ if (this.disposedValue)
+ {
+ throw new ObjectDisposedException(nameof(Recorder));
+ }
+
+ AzureKinectAddCustomVideoTrackException.ThrowIfNotSuccess(() => NativeMethods.k4a_record_add_custom_video_track(
+ this.handle,
+ trackName,
+ codecId,
+ codecContext,
+ (UIntPtr)codecContext.Length,
+ trackSettings));
+ }
+ }
+
+ ///
+ /// Adds custom subtitle tracks to the recording.
+ ///
+ /// The name of the custom subtitle track to be added.
+ /// A UTF8 null terminated string containing the codec ID of the track.
+ /// Some of the existing formats are listed here: https://www.matroska.org/technical/specs/codecid/index.html. The codec ID can also be custom defined by the user.
+ /// Subtitle codec ID's should start with 'S_'.
+ /// The codec context is a codec-specific buffer that contains any required codec metadata that is only known to the codec.It is mapped to the matroska 'CodecPrivate' element.
+ /// Additional metadata for the subtitle track. If null, the default settings will be used.
+ ///
+ /// Built-in subtitle tracks like the IMU track will be created automatically when the k4a_record_add_imu_track() API is
+ /// called.This API can be used to add additional subtitle tracks to save custom data.
+ ///
+ /// Track names must be ALL CAPS and may only contain A-Z, 0-9, '-' and '_'.
+ ///
+ /// All tracks need to be added before the recording header is written.
+ ///
+ /// Call k4a_record_write_custom_track_data() with the same track_name to write data to this track.
+ public void AddCustomSubtitleTrack(
+ string trackName,
+ string codecId,
+ byte[] codecContext,
+ RecordSubtitleSettings trackSettings)
+ {
+ lock (this)
+ {
+ if (this.disposedValue)
+ {
+ throw new ObjectDisposedException(nameof(Recorder));
+ }
+
+ AzureKinectAddCustomSubtitleTrackException.ThrowIfNotSuccess(() => NativeMethods.k4a_record_add_custom_subtitle_track(
+ this.handle,
+ trackName,
+ codecId,
+ codecContext,
+ (UIntPtr)codecContext.Length,
+ trackSettings));
+ }
+ }
+
+ ///
+ /// Writes the recording header and metadata to file.
+ ///
+ ///
+ /// This must be called before captures or any track data can be written.
+ ///
+ public void WriteHeader()
+ {
+ lock (this)
+ {
+ if (this.disposedValue)
+ {
+ throw new ObjectDisposedException(nameof(Recorder));
+ }
+
+ AzureKinectWriteHeaderException.ThrowIfNotSuccess(() => NativeMethods.k4a_record_write_header(this.handle));
+ }
+ }
+
+ ///
+ /// Writes a capture to the recording file.
+ ///
+ /// Capture containing data to write.
+ ///
+ /// Captures must be written in increasing order of timestamp, and the file's header must already be written.
+ ///
+ /// k4a_record_write_capture() will write all images in the capture to the corresponding tracks in the recording file.
+ /// If any of the images fail to write, other images will still be written before a failure is returned.
+ ///
+ public void WriteCapture(Capture capture)
+ {
+ lock (this)
+ {
+ if (this.disposedValue)
+ {
+ throw new ObjectDisposedException(nameof(Recorder));
+ }
+
+ if (capture == null)
+ {
+ throw new ArgumentNullException(nameof(capture));
+ }
+
+ using (Capture reference = capture.Reference())
+ {
+ AzureKinectWriteCaptureException.ThrowIfNotSuccess(() => NativeMethods.k4a_record_write_capture(this.handle, reference.Handle));
+ }
+ }
+ }
+
+ ///
+ /// Writes an IMU sample to the recording.
+ ///
+ /// Sample with the IMU data.
+ public void WriteImuSample(ImuSample imuSample)
+ {
+ lock (this)
+ {
+ if (this.disposedValue)
+ {
+ throw new ObjectDisposedException(nameof(Recorder));
+ }
+
+ if (imuSample == null)
+ {
+ throw new ArgumentNullException(nameof(imuSample));
+ }
+
+ NativeMethods.k4a_imu_sample_t sample = new NativeMethods.k4a_imu_sample_t()
+ {
+ temperature = imuSample.Temperature,
+ acc_sample = imuSample.AccelerometerSample,
+ acc_timestamp_usec = checked((ulong)imuSample.AccelerometerTimestamp.Ticks / 10),
+ gyro_sample = imuSample.GyroSample,
+ gyro_timestamp_usec = checked((ulong)imuSample.GyroTimestamp.Ticks / 10),
+ };
+
+ AzureKinectWriteImuSampleException.ThrowIfNotSuccess(() => NativeMethods.k4a_record_write_imu_sample(this.handle, sample));
+ }
+ }
+
+ ///
+ /// Writes data for a custom track to file.
+ ///
+ /// The name of the custom track that the data is going to be written to.
+ /// The timestamp for the custom track data. This timestamp should be in the same time domain as the device timestamp used for recording.
+ /// The buffer of custom track data.
+ ///
+ /// Custom track data must be written in increasing order of timestamp, and the file's header must already be written.
+ /// When writing custom track data at the same time as captures or IMU data, the custom data should be within 1 second of
+ /// the most recently written timestamp.
+ ///
+ public void WriteCustomTrackData(
+ string trackName,
+ TimeSpan deviceTimestamp,
+ byte[] customData)
+ {
+ lock (this)
+ {
+ if (this.disposedValue)
+ {
+ throw new ObjectDisposedException(nameof(Recorder));
+ }
+
+ if (trackName == null)
+ {
+ throw new ArgumentNullException(nameof(trackName));
+ }
+
+ if (customData == null)
+ {
+ throw new ArgumentNullException(nameof(customData));
+ }
+
+ AzureKinectWriteCustomTrackDataException.ThrowIfNotSuccess(() => NativeMethods.k4a_record_write_custom_track_data(
+ this.handle,
+ trackName,
+ checked((ulong)deviceTimestamp.Ticks / 10),
+ customData,
+ (UIntPtr)customData.Length));
+ }
+ }
+
+ ///
+ /// Flushes all pending recording data to disk.
+ ///
+ ///
+ /// If an error occurs, best effort is made to flush as much data to disk as possible, but the integrity of the file is not guaranteed.
+ public void Flush()
+ {
+ lock (this)
+ {
+ if (this.disposedValue)
+ {
+ throw new ObjectDisposedException(nameof(Recorder));
+ }
+
+ AzureKinectFlushException.ThrowIfNotSuccess(() => NativeMethods.k4a_record_flush(this.handle));
+ }
+ }
+
+ ///
+ public void Dispose()
+ {
+ this.Dispose(true);
+
+ GC.SuppressFinalize(this);
+ }
+
+ ///
+ /// Handle the disposing of the object.
+ ///
+ /// true when called by Dispose(), false when called by the finalizer.
+ protected virtual void Dispose(bool disposing)
+ {
+ lock (this)
+ {
+ this.handle.Close();
+
+ this.disposedValue = true;
+ }
+ }
+ }
+}
diff --git a/src/csharp/SDK/Allocator.cs b/src/csharp/SDK/Allocator.cs
index 4b6419dc2..490e26cfc 100644
--- a/src/csharp/SDK/Allocator.cs
+++ b/src/csharp/SDK/Allocator.cs
@@ -92,7 +92,9 @@ public bool UseManagedAllocator
AzureKinectException.ThrowIfNotSuccess(() => NativeMethods.k4a_set_allocator(this.allocateDelegate, this.freeDelegate));
this.hooked = true;
}
+#pragma warning disable CA1031 // Do not catch general exception types
catch (Exception)
+#pragma warning restore CA1031 // Do not catch general exception types
{
// Don't fail if we can't set the allocator since this code path is called during the global type
// initialization. A failure to set the allocator is also not fatal, but will only cause a performance
diff --git a/src/csharp/SDK/Capture.cs b/src/csharp/SDK/Capture.cs
index b69844efb..ec1b767ce 100644
--- a/src/csharp/SDK/Capture.cs
+++ b/src/csharp/SDK/Capture.cs
@@ -37,6 +37,20 @@ public Capture()
Allocator.Singleton.RegisterForDisposal(this);
}
+ ///
+ /// Initializes a new instance of the class from an existing native handle.
+ ///
+ /// If false, the constructor will take a new reference on the handle.
+ /// Native handle to a k4a_capture_t.
+ ///
+ /// Disposing this Capture will always release a reference on the handle.
+ /// If is true, the handle may not be owned by any existing Capture object.
+ ///
+ public Capture(bool takeOwnership, IntPtr handle)
+ : this(new NativeMethods.k4a_capture_t(takeOwnership, handle))
+ {
+ }
+
///
/// Initializes a new instance of the class.
///
@@ -169,6 +183,32 @@ public float Temperature
}
}
+ ///
+ /// Gets the native handle.
+ ///
+ /// This is the value of the k4a_capture_t handle of the native library.
+ ///
+ /// This handle value can be used to interoperate with other native libraries that use
+ /// Azure Kinect objects.
+ ///
+ /// When using this handle value, the caller is responsible for ensuring that the
+ /// Capture object does not become disposed.
+ public IntPtr Handle
+ {
+ get
+ {
+ lock (this)
+ {
+ if (this.disposedValue)
+ {
+ throw new ObjectDisposedException(nameof(Capture));
+ }
+
+ return this.handle.DangerousGetHandle();
+ }
+ }
+ }
+
///
/// Creates a duplicate reference to the same Capture.
///
diff --git a/src/csharp/SDK/Device.cs b/src/csharp/SDK/Device.cs
index fba4d2df8..d091dc3d9 100644
--- a/src/csharp/SDK/Device.cs
+++ b/src/csharp/SDK/Device.cs
@@ -167,6 +167,32 @@ public HardwareVersion Version
}
}
+ ///
+ /// Gets the native handle.
+ ///
+ /// This is the value of the k4a_device_t handle of the native library.
+ ///
+ /// This handle value can be used to interoperate with other native libraries that use
+ /// Azure Kinect objects.
+ ///
+ /// When using this handle value, the caller is responsible for ensuring that the
+ /// Device object does not become disposed.
+ public IntPtr Handle
+ {
+ get
+ {
+ lock (this)
+ {
+ if (this.disposedValue)
+ {
+ throw new ObjectDisposedException(nameof(Device));
+ }
+
+ return this.handle.DangerousGetHandle();
+ }
+ }
+ }
+
///
/// Gets the number of currently connected devices.
///
@@ -274,7 +300,9 @@ public Capture GetCapture(TimeSpan timeout)
throw new ObjectDisposedException(nameof(Device));
}
+#pragma warning disable CA1508 // Avoid dead conditional code
using (LoggingTracer tracer = new LoggingTracer())
+#pragma warning restore CA1508 // Avoid dead conditional code
{
NativeMethods.k4a_wait_result_t result = NativeMethods.k4a_device_get_capture(this.handle, out NativeMethods.k4a_capture_t capture, (int)timeout.TotalMilliseconds);
@@ -328,7 +356,9 @@ public ImuSample GetImuSample(TimeSpan timeout)
throw new ObjectDisposedException(nameof(Device));
}
+#pragma warning disable CA1508 // Avoid dead conditional code
using (LoggingTracer tracer = new LoggingTracer())
+#pragma warning restore CA1508 // Avoid dead conditional code
{
NativeMethods.k4a_imu_sample_t sample = new NativeMethods.k4a_imu_sample_t();
NativeMethods.k4a_wait_result_t result = NativeMethods.k4a_device_get_imu_sample(this.handle, sample, (int)timeout.TotalMilliseconds);
@@ -503,6 +533,27 @@ public void Dispose()
GC.SuppressFinalize(this);
}
+ ///
+ /// Gets the native handle.
+ ///
+ /// The native handle that is wrapped by this device.
+ /// The function is dangerous because there is no guarantee that the
+ /// handle will not be disposed once it is retrieved. This should only be called
+ /// by code that can ensure that the Capture object will not be disposed on another
+ /// thread.
+ internal NativeMethods.k4a_device_t DangerousGetHandle()
+ {
+ lock (this)
+ {
+ if (this.disposedValue)
+ {
+ throw new ObjectDisposedException(nameof(Device));
+ }
+
+ return this.handle;
+ }
+ }
+
///
/// Releases unmanaged and - optionally - managed resources.
///
@@ -513,12 +564,17 @@ protected virtual void Dispose(bool disposing)
{
if (disposing)
{
- Allocator.Singleton.UnregisterForDisposal(this);
+ // Callers of DangerousGetHandle will lock this Device object
+ // to ensure the handle isn't disposed while in use.
+ lock (this)
+ {
+ Allocator.Singleton.UnregisterForDisposal(this);
- this.handle.Close();
- this.handle = null;
+ this.handle.Close();
+ this.handle = null;
- this.disposedValue = true;
+ this.disposedValue = true;
+ }
}
}
}
diff --git a/src/csharp/SDK/ILoggingProvider.cs b/src/csharp/SDK/ILoggingProvider.cs
new file mode 100644
index 000000000..0a03df569
--- /dev/null
+++ b/src/csharp/SDK/ILoggingProvider.cs
@@ -0,0 +1,30 @@
+//------------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+//
+//------------------------------------------------------------------------------
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Microsoft.Azure.Kinect.Sensor
+{
+ ///
+ /// An interface for trace logging providers from the native SDK.
+ ///
+ public interface ILoggingProvider
+ {
+#pragma warning disable CA1003 // Use generic event handler instances
+ ///
+ /// Occurs when the native SDK delivers a debug message.
+ ///
+ event Action LogMessage;
+#pragma warning restore CA1003 // Use generic event handler instances
+
+ ///
+ /// Gets the name of the layer providing the messages.
+ ///
+ string ProviderName { get; }
+ }
+}
diff --git a/src/csharp/SDK/Image.cs b/src/csharp/SDK/Image.cs
index 8ba3e6217..33475010d 100644
--- a/src/csharp/SDK/Image.cs
+++ b/src/csharp/SDK/Image.cs
@@ -83,6 +83,20 @@ public Image(ImageFormat format, int widthPixels, int heightPixels)
#pragma warning restore CA2000 // Dispose objects before losing scope
}
+ ///
+ /// Initializes a new instance of the class from an existing native handle.
+ ///
+ /// If false, the constructor will take a new reference on the handle.
+ /// Native handle to a k4a_image_t.
+ ///
+ /// Disposing this Image will always release a reference on the handle.
+ /// If is true, the handle may not be owned by any existing Image object.
+ ///
+ public Image(bool takeOwnership, IntPtr handle)
+ : this(new NativeMethods.k4a_image_t(takeOwnership, handle))
+ {
+ }
+
///
/// Initializes a new instance of the class.
///
@@ -468,6 +482,32 @@ public int WhiteBalance
}
}
+ ///
+ /// Gets the native handle.
+ ///
+ /// This is the value of the k4a_image_t handle of the native library.
+ ///
+ /// This handle value can be used to interoperate with other native libraries that use
+ /// Azure Kinect objects.
+ ///
+ /// When using this handle value, the caller is responsible for ensuring that the
+ /// Image object does not become disposed.
+ public IntPtr Handle
+ {
+ get
+ {
+ lock (this)
+ {
+ if (this.disposedValue)
+ {
+ throw new ObjectDisposedException(nameof(Image));
+ }
+
+ return this.handle.DangerousGetHandle();
+ }
+ }
+ }
+
///
/// Gets the pixels of the image.
///
diff --git a/src/csharp/SDK/LogMessage.cs b/src/csharp/SDK/LogMessage.cs
index 820b2a8e2..f60f37619 100644
--- a/src/csharp/SDK/LogMessage.cs
+++ b/src/csharp/SDK/LogMessage.cs
@@ -21,7 +21,7 @@ public class LogMessage
/// The file name of the source file that generated the message.
/// The line number of the source file that generated the message.
/// The messaged generated by the Azure Kinect SDK.
- internal LogMessage(DateTime time, LogLevel logLevel, string fileName, int line, string message)
+ public LogMessage(DateTime time, LogLevel logLevel, string fileName, int line, string message)
{
this.Time = time;
this.LogLevel = logLevel;
diff --git a/src/csharp/SDK/Logger.cs b/src/csharp/SDK/Logger.cs
index 50dfecad8..a919fa087 100644
--- a/src/csharp/SDK/Logger.cs
+++ b/src/csharp/SDK/Logger.cs
@@ -16,14 +16,15 @@ public static class Logger
{
private static readonly object SyncRoot = new object();
private static readonly NativeMethods.k4a_logging_message_cb_t DebugMessageHandler = OnDebugMessage;
+ private static readonly LoggerProvider LoggerProviderValue = new LoggerProvider();
private static bool isInitialized;
- private static event Action LogMessageHandlers;
-
+#pragma warning disable CA1003 // Use generic event handler instances
///
/// Occurs when the Azure Kinect Sensor SDK delivers a debug message.
///
public static event Action LogMessage
+#pragma warning restore CA1003 // Use generic event handler instances
{
add
{
@@ -47,6 +48,19 @@ public static event Action LogMessage
}
}
+ private static event Action LogMessageHandlers;
+
+ ///
+ /// Gets the interface for reading log messages.
+ ///
+ public static ILoggingProvider LogProvider
+ {
+ get
+ {
+ return Logger.LoggerProviderValue;
+ }
+ }
+
///
/// Initializes the class to begin receiving messages from the Azure Kinect Sensor SDK.
///
@@ -117,5 +131,23 @@ private static void CurrentDomain_Exit(object sender, EventArgs e)
Trace.WriteLine("Failed to close the debug message handler");
}
}
+
+ private class LoggerProvider : ILoggingProvider
+ {
+ public event Action LogMessage
+ {
+ add
+ {
+ Logger.LogMessage += value;
+ }
+
+ remove
+ {
+ Logger.LogMessage -= value;
+ }
+ }
+
+ public string ProviderName => "Azure Kinect SDK";
+ }
}
}
diff --git a/src/csharp/SDK/Native/LoggingTracer.cs b/src/csharp/SDK/Native/LoggingTracer.cs
index 265ab604c..9ce9d9452 100644
--- a/src/csharp/SDK/Native/LoggingTracer.cs
+++ b/src/csharp/SDK/Native/LoggingTracer.cs
@@ -14,21 +14,41 @@ namespace Microsoft.Azure.Kinect.Sensor
/// Represents a tracer for capturing thread specific logging messages for tracing native calls
/// into the Azure Kinect Sensor SDK.
///
- internal class LoggingTracer : IDisposable
+ public class LoggingTracer : IDisposable
{
private readonly int threadId;
+ private readonly LogLevel minLevel;
private bool disposed;
private List messages;
+ private ILoggingProvider[] loggingProviders;
+
///
/// Initializes a new instance of the class.
///
public LoggingTracer()
+ : this(LogLevel.Warning, Logger.LogProvider)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Minimum level of messages to capture.
+ /// Set of logging providers to capture from.
+ public LoggingTracer(LogLevel minLevel, params ILoggingProvider[] loggingProvider)
{
this.messages = new List();
this.threadId = Thread.CurrentThread.ManagedThreadId;
- Logger.LogMessage += this.Logger_LogMessage;
+ this.minLevel = minLevel;
+
+ this.loggingProviders = (ILoggingProvider[])loggingProvider.Clone();
+
+ foreach (ILoggingProvider provider in this.loggingProviders)
+ {
+ provider.LogMessage += this.Logger_LogMessage;
+ }
}
///
@@ -71,7 +91,12 @@ protected virtual void Dispose(bool disposing)
{
if (disposing)
{
- Logger.LogMessage -= this.Logger_LogMessage;
+ foreach (ILoggingProvider provider in this.loggingProviders)
+ {
+ provider.LogMessage -= this.Logger_LogMessage;
+ }
+
+ this.loggingProviders = null;
// There are no longer any tracers on this thread. Clear the message list to allow the memory to be freed.
this.messages = null;
@@ -89,6 +114,12 @@ private void Logger_LogMessage(LogMessage logMessage)
return;
}
+ if (logMessage.LogLevel > this.minLevel)
+ {
+ // The log messages are too verbose. Ignore them.
+ return;
+ }
+
this.messages.Add(logMessage);
}
}
diff --git a/src/csharp/SDK/Native/NativeMethods.cs b/src/csharp/SDK/Native/NativeMethods.cs
index 5ea3ec5de..b0c064703 100644
--- a/src/csharp/SDK/Native/NativeMethods.cs
+++ b/src/csharp/SDK/Native/NativeMethods.cs
@@ -5,6 +5,8 @@
//
//------------------------------------------------------------------------------
using System;
+using System.Globalization;
+using System.Linq.Expressions;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Text;
@@ -440,6 +442,17 @@ protected override bool ReleaseHandle()
public class k4a_capture_t : Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid
{
+ public k4a_capture_t(bool takeOwnership, IntPtr handle)
+ : base(true)
+ {
+ if (!takeOwnership)
+ {
+ NativeMethods.k4a_image_reference(handle);
+ }
+
+ this.handle = handle;
+ }
+
private k4a_capture_t()
: base(true)
{
@@ -464,6 +477,17 @@ protected override bool ReleaseHandle()
public class k4a_image_t : Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid
{
+ public k4a_image_t(bool takeOwnership, IntPtr handle)
+ : base(true)
+ {
+ if (!takeOwnership)
+ {
+ NativeMethods.k4a_image_reference(handle);
+ }
+
+ this.handle = handle;
+ }
+
private k4a_image_t()
: base(true)
{
diff --git a/src/csharp/Tests/Record.UnitTests/LoopbackTests.cs b/src/csharp/Tests/Record.UnitTests/LoopbackTests.cs
new file mode 100644
index 000000000..d80b5b94b
--- /dev/null
+++ b/src/csharp/Tests/Record.UnitTests/LoopbackTests.cs
@@ -0,0 +1,216 @@
+using System;
+using Microsoft.Azure.Kinect.Sensor;
+using Microsoft.Azure.Kinect.Sensor.Record;
+using NUnit.Framework;
+
+namespace Tests
+{
+ ///
+ /// Loopback Tests write to a recording, and then read the recording back to verify the API.
+ ///
+ public class LoopbackTests
+ {
+ private string recordingPath;
+
+ ///
+ /// Allocate a path for the recording.
+ ///
+ [SetUp]
+ public void Setup()
+ {
+ this.recordingPath = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "testfile.mkv");
+ }
+
+ ///
+ /// Delete the temporary recording.
+ ///
+ [TearDown]
+ public void TearDown()
+ {
+ System.IO.File.Delete(this.recordingPath);
+ }
+
+ ///
+ /// Writes each of the data types to a file and reads them back.
+ /// Verfies as many properties as possible.
+ ///
+ [Test]
+ public void LoopbackTest1()
+ {
+ DeviceConfiguration deviceConfiguration = new DeviceConfiguration()
+ {
+ CameraFPS = FPS.FPS30,
+ ColorFormat = ImageFormat.ColorNV12,
+ ColorResolution = ColorResolution.R720p,
+ DepthDelayOffColor = TimeSpan.FromMilliseconds(123),
+ DepthMode = DepthMode.NFOV_2x2Binned,
+ DisableStreamingIndicator = true,
+ SuboridinateDelayOffMaster = TimeSpan.FromMilliseconds(456),
+ SynchronizedImagesOnly = true,
+ WiredSyncMode = WiredSyncMode.Subordinate,
+ };
+
+ using (Recorder record = Recorder.Create(this.recordingPath, null, deviceConfiguration))
+#pragma warning restore CA1508 // Avoid dead conditional code
+ {
+ record.AddImuTrack();
+ record.AddCustomVideoTrack("CUSTOM_VIDEO", "V_CUSTOM1", new byte[] { 1, 2, 3 }, new RecordVideoSettings() { FrameRate = 1, Height = 10, Width = 20 });
+ record.AddCustomSubtitleTrack("CUSTOM_SUBTITLE", "S_CUSTOM1", new byte[] { 4, 5, 6, 7 }, new RecordSubtitleSettings() { HighFrequencyData = false });
+ record.AddTag("MYTAG1", "one");
+ record.AddTag("MYTAG2", "two");
+
+ record.WriteHeader();
+
+ for (int i = 0; i < 10; i++)
+ {
+ double timeStamp = 10.0 + (i * 1.0);
+
+#pragma warning disable CA1508 // Avoid dead conditional code
+ using (Capture c = new Capture())
+#pragma warning restore CA1508 // Avoid dead conditional code
+ {
+ c.Color = new Image(ImageFormat.ColorNV12, 1280, 720);
+ c.IR = new Image(ImageFormat.IR16, 320, 288);
+ c.Depth = new Image(ImageFormat.Depth16, 320, 288);
+ c.Temperature = 25.0f;
+ c.Color.DeviceTimestamp = TimeSpan.FromSeconds(timeStamp);
+ c.Depth.DeviceTimestamp = TimeSpan.FromSeconds(timeStamp) + deviceConfiguration.DepthDelayOffColor;
+ c.IR.DeviceTimestamp = TimeSpan.FromSeconds(timeStamp) + deviceConfiguration.DepthDelayOffColor;
+
+ c.Color.Exposure = TimeSpan.FromMilliseconds(12);
+ c.Color.ISOSpeed = 100;
+ c.Color.SystemTimestampNsec = System.Diagnostics.Stopwatch.GetTimestamp();
+ c.Color.WhiteBalance = 2;
+
+ c.Depth.SystemTimestampNsec = System.Diagnostics.Stopwatch.GetTimestamp();
+
+ c.IR.SystemTimestampNsec = System.Diagnostics.Stopwatch.GetTimestamp();
+
+ record.WriteCapture(c);
+ }
+
+ for (int y = 0; y < 10; y++)
+ {
+ ImuSample imuSample = new ImuSample()
+ {
+ AccelerometerSample = new System.Numerics.Vector3(1.0f, 2.0f, 3.0f),
+ GyroSample = new System.Numerics.Vector3(4.0f, 5.0f, 6.0f),
+ AccelerometerTimestamp = TimeSpan.FromSeconds(timeStamp + (0.1 * y)),
+ GyroTimestamp = TimeSpan.FromSeconds(timeStamp + (0.1 * y)),
+ Temperature = 26.0f,
+ };
+
+ record.WriteImuSample(imuSample);
+ }
+
+ byte[] customData = new byte[i + 1];
+ for (int x = 0; x < customData.Length; x++)
+ {
+ customData[x] = (byte)(i + x);
+ }
+
+ record.WriteCustomTrackData("CUSTOM_VIDEO", TimeSpan.FromSeconds(timeStamp), customData);
+
+ for (int x = 0; x < customData.Length; x++)
+ {
+ customData[x] = (byte)(i + x + 1);
+ }
+
+ record.WriteCustomTrackData("CUSTOM_SUBTITLE", TimeSpan.FromSeconds(timeStamp), customData);
+
+ record.Flush();
+ }
+ }
+
+ using (Playback playback = Playback.Open(this.recordingPath))
+ {
+ Assert.IsTrue(playback.CheckTrackExists("CUSTOM_VIDEO"));
+ Assert.IsTrue(playback.CheckTrackExists("CUSTOM_SUBTITLE"));
+ Assert.AreEqual("V_CUSTOM1", playback.GetTrackCodecId("CUSTOM_VIDEO"));
+ Assert.AreEqual(new byte[] { 1, 2, 3 }, playback.GetTrackCodecContext("CUSTOM_VIDEO"));
+
+ Assert.AreEqual("one", playback.GetTag("MYTAG1"));
+ Assert.AreEqual("two", playback.GetTag("MYTAG2"));
+
+ Assert.AreEqual(FPS.FPS30, playback.RecordConfiguration.CameraFPS);
+ Assert.AreEqual(ImageFormat.ColorNV12, playback.RecordConfiguration.ColorFormat);
+ Assert.AreEqual(TimeSpan.FromMilliseconds(456), playback.RecordConfiguration.SubordinateDelayOffMaster);
+
+ Assert.IsFalse(playback.Calibration.HasValue);
+
+ for (int i = 0; i < 10; i++)
+ {
+ double timeStamp = 10.0 + (i * 1.0);
+
+ using (Capture c = playback.GetNextCapture())
+ {
+ // Not captured in recording
+ // Assert.AreEqual(25.0f, c.Temperature);
+ Assert.AreEqual(ImageFormat.ColorNV12, c.Color.Format);
+ Assert.AreEqual(1280, c.Color.WidthPixels);
+ Assert.AreEqual(720, c.Color.HeightPixels);
+
+ Assert.AreEqual(TimeSpan.FromSeconds(timeStamp), c.Color.DeviceTimestamp);
+ Assert.AreEqual(TimeSpan.FromSeconds(timeStamp) + deviceConfiguration.DepthDelayOffColor, c.Depth.DeviceTimestamp);
+ Assert.AreEqual(TimeSpan.FromSeconds(timeStamp) + deviceConfiguration.DepthDelayOffColor, c.IR.DeviceTimestamp);
+
+ // Not captured in recording
+ // Assert.AreEqual(TimeSpan.FromMilliseconds(12), c.Color.Exposure);
+
+ // Not captured in recording
+ // Assert.AreEqual(100, c.Color.ISOSpeed);
+ Assert.AreEqual(0, c.Color.SystemTimestampNsec);
+
+ // Not captured in recording
+ // Assert.AreEqual(2, c.Color.WhiteBalance);
+ }
+
+ for (int y = 0; y < 10; y++)
+ {
+ ImuSample imuSample = new ImuSample()
+ {
+ AccelerometerSample = new System.Numerics.Vector3(1.0f, 2.0f, 3.0f),
+ GyroSample = new System.Numerics.Vector3(4.0f, 5.0f, 6.0f),
+ AccelerometerTimestamp = TimeSpan.FromSeconds(timeStamp + 0.1 * y),
+ GyroTimestamp = TimeSpan.FromSeconds(timeStamp + 0.1 * y),
+ Temperature = 26.0f,
+ };
+
+ ImuSample readSample = playback.GetNextImuSample();
+
+ Assert.AreEqual(imuSample.AccelerometerSample, readSample.AccelerometerSample);
+ Assert.AreEqual(imuSample.GyroSample, readSample.GyroSample);
+ Assert.AreEqual(imuSample.AccelerometerTimestamp, readSample.AccelerometerTimestamp);
+ Assert.AreEqual(imuSample.GyroTimestamp, readSample.GyroTimestamp);
+
+ // Not captured in recording
+ // Assert.AreEqual(imuSample.Temperature, readSample.Temperature);
+ }
+
+ byte[] customData = new byte[i + 1];
+ for (int x = 0; x < customData.Length; x++)
+ {
+ customData[x] = (byte)(i + x);
+ }
+
+ using (DataBlock videoBlock = playback.GetNextDataBlock("CUSTOM_VIDEO"))
+ {
+ Assert.AreEqual(customData, videoBlock.Memory.ToArray());
+ Assert.AreEqual(TimeSpan.FromSeconds(timeStamp), videoBlock.DeviceTimestamp);
+ }
+
+ for (int x = 0; x < customData.Length; x++)
+ {
+ customData[x] = (byte)(i + x + 1);
+ }
+
+ using (DataBlock subtitleBlock = playback.GetNextDataBlock("CUSTOM_SUBTITLE"))
+ {
+ Assert.AreEqual(customData, subtitleBlock.Memory.ToArray());
+ Assert.AreEqual(TimeSpan.FromSeconds(timeStamp), subtitleBlock.DeviceTimestamp);
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/csharp/Tests/Record.UnitTests/Microsoft.Azure.Kinect.Sensor.Record.UnitTests.csproj b/src/csharp/Tests/Record.UnitTests/Microsoft.Azure.Kinect.Sensor.Record.UnitTests.csproj
new file mode 100644
index 000000000..4f7a9e27f
--- /dev/null
+++ b/src/csharp/Tests/Record.UnitTests/Microsoft.Azure.Kinect.Sensor.Record.UnitTests.csproj
@@ -0,0 +1,65 @@
+
+
+
+
+ netcoreapp2.1
+
+ false
+
+ x64;x86
+ false
+ ..\..\AzureKinectSensorSDK.ruleset
+ $(BaseOutputPath)\$(AssemblyName)\
+
+
+
+ false
+
+
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+
+
+
+ stylecop.json
+
+
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/record/sdk/record.cpp b/src/record/sdk/record.cpp
index 97d8ede60..aed6685e1 100644
--- a/src/record/sdk/record.cpp
+++ b/src/record/sdk/record.cpp
@@ -14,6 +14,13 @@
using namespace k4arecord;
using namespace LIBMATROSKA_NAMESPACE;
+k4a_result_t k4a_record_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);
+}
+
k4a_result_t k4a_record_create(const char *path,
k4a_device_t device,
const k4a_device_configuration_t device_config,
@@ -373,7 +380,10 @@ k4a_result_t k4a_record_add_tag(const k4a_record_t recording_handle, const char
return K4A_RESULT_FAILED;
}
- add_tag(context, name, value);
+ if (NULL == add_tag(context, name, value))
+ {
+ return K4A_RESULT_FAILED;
+ }
return K4A_RESULT_SUCCEEDED;
}