diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 000000000..b37b5f28b --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,4 @@ +# Data science would like to know about all schema changes Engineering plan to make to SQL databases. This automates the process and is intended to be used by Data Science as a FYI of an incoming change as opposed to a review. +# https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners +* @vald-green/code-owners-harald +*.sql @vald-green/code-owners-harald @vald-green/code-owners-data-science diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 90bc80691..5591cbf3a 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -4,6 +4,8 @@ --> ## Fixes # +Related DevOps Ticket: AB# + ### Description of the changes: - - diff --git a/docs/releasing.md b/docs/releasing.md index 0d8457f91..01bab10b0 100644 --- a/docs/releasing.md +++ b/docs/releasing.md @@ -89,59 +89,62 @@ used to create a released build. Checkout the commit that matches the release la 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: | | | | +----------------------------------------------------------------------------- | ------------------ | ------------------ | ------------------ | ------------------ | ----------------- +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/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..787b7d55a --- /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()) + { + 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..f4e449bdd --- /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()) + { + 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..2d157d264 --- /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()) + { + 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..5a58ff402 --- /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()) + { + 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..10bb009ad --- /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()) + { + 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..2acd0be5f --- /dev/null +++ b/src/csharp/Record/Exceptions/AzureKinectCreateRecordingException.cs @@ -0,0 +1,120 @@ +//------------------------------------------------------------------------------ +// +// 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()) + { + 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..02f14be0f --- /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()) + { + 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..7186c30bc --- /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()) + { + 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..2065ffc65 --- /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()) + { + 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..c5b532785 --- /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()) + { + 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..2a2655eab --- /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()) + { + 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..155f9ec89 --- /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()) + { + 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..67ff7d4b2 --- /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()) + { + 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..d668e0b6d --- /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()) + { + 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..fa38f4d91 --- /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()) + { + 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..c6e5972e4 --- /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()) + { + 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..e866e22c0 --- /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()) + { + 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..a62bf0054 --- /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()) + { + 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..ad92032c7 --- /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()) + { + 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..cee354d23 --- /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()) + { + 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..d29fbffbf --- /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()) + { + 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..dedbd03d4 --- /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()) + { + 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..3b7bdac38 --- /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()) + { + 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..15f2f26ae --- /dev/null +++ b/src/csharp/Record/NativeMethods.cs @@ -0,0 +1,363 @@ +//------------------------------------------------------------------------------ +// +// 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, 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..578c17432 --- /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()) +#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/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/Image.cs b/src/csharp/SDK/Image.cs index 8ba3e6217..185aaad0e 100644 --- a/src/csharp/SDK/Image.cs +++ b/src/csharp/SDK/Image.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. @@ -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/Logger.cs b/src/csharp/SDK/Logger.cs index 50dfecad8..3888f8e93 100644 --- a/src/csharp/SDK/Logger.cs +++ b/src/csharp/SDK/Logger.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. @@ -18,12 +18,12 @@ public static class Logger private static readonly NativeMethods.k4a_logging_message_cb_t DebugMessageHandler = OnDebugMessage; 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 +47,8 @@ public static event Action LogMessage } } + private static event Action LogMessageHandlers; + /// /// Initializes the class to begin receiving messages from the Azure Kinect Sensor SDK. /// diff --git a/src/csharp/SDK/Native/LoggingTracer.cs b/src/csharp/SDK/Native/LoggingTracer.cs index 265ab604c..b79d94ce6 100644 --- a/src/csharp/SDK/Native/LoggingTracer.cs +++ b/src/csharp/SDK/Native/LoggingTracer.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. @@ -14,7 +14,7 @@ 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; diff --git a/src/csharp/SDK/Native/NativeMethods.cs b/src/csharp/SDK/Native/NativeMethods.cs index def287e7b..d621ae3b9 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; @@ -460,6 +462,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) { @@ -484,6 +497,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 b6f0baffb..e0dfa1f2b 100644 --- a/src/record/sdk/record.cpp +++ b/src/record/sdk/record.cpp @@ -376,7 +376,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; }