From 176e9e2bb2b1e22a7ceaf6bcbec05c7891dd6eac Mon Sep 17 00:00:00 2001 From: Brent Allen Date: Thu, 3 Oct 2019 14:26:12 -0700 Subject: [PATCH 01/16] Added begining of record/playback for c# --- ...re.Kinect.Sensor.Examples.Recording.csproj | 14 + src/csharp/Examples/Recording/Program.cs | 42 ++ src/csharp/K4a.sln | 61 +++ .../Exceptions/AzureKinectAddTagException.cs | 118 ++++++ .../AzureKinectCreateRecordingException.cs | 119 ++++++ .../Exceptions/AzureKinectRecordException.cs | 142 +++++++ ...icrosoft.Azure.Kinect.Sensor.Record.csproj | 11 + src/csharp/Record/NativeMethods.cs | 379 ++++++++++++++++++ src/csharp/Record/Record.cs | 139 +++++++ src/csharp/SDK/Capture.cs | 26 ++ src/csharp/SDK/Device.cs | 60 ++- src/csharp/SDK/Native/LoggingTracer.cs | 2 +- src/csharp/SDK/Native/NativeMethods.cs | 36 ++ 13 files changed, 1144 insertions(+), 5 deletions(-) create mode 100644 src/csharp/Examples/Recording/Microsoft.Azure.Kinect.Sensor.Examples.Recording.csproj create mode 100644 src/csharp/Examples/Recording/Program.cs create mode 100644 src/csharp/Record/Exceptions/AzureKinectAddTagException.cs create mode 100644 src/csharp/Record/Exceptions/AzureKinectCreateRecordingException.cs create mode 100644 src/csharp/Record/Exceptions/AzureKinectRecordException.cs create mode 100644 src/csharp/Record/Microsoft.Azure.Kinect.Sensor.Record.csproj create mode 100644 src/csharp/Record/NativeMethods.cs create mode 100644 src/csharp/Record/Record.cs 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..f21e48217 --- /dev/null +++ b/src/csharp/Examples/Recording/Microsoft.Azure.Kinect.Sensor.Examples.Recording.csproj @@ -0,0 +1,14 @@ + + + + Exe + netcoreapp2.2 + dotnetrecording + + + + + + + + diff --git a/src/csharp/Examples/Recording/Program.cs b/src/csharp/Examples/Recording/Program.cs new file mode 100644 index 000000000..647d0b803 --- /dev/null +++ b/src/csharp/Examples/Recording/Program.cs @@ -0,0 +1,42 @@ +using System; +using Microsoft.Azure.Kinect.Sensor; +using Microsoft.Azure.Kinect.Sensor.Record; + +namespace Recording +{ + class Program + { + static void Main(string[] args) + { + int frame = 0; + + Console.WriteLine("Recording from device."); + + DeviceConfiguration configuration = new DeviceConfiguration() + { + CameraFPS = FPS.FPS30, + ColorFormat = ImageFormat.ColorMJPG, + ColorResolution = ColorResolution.R720p, + DepthMode = DepthMode.NFOV_2x2Binned + }; + + using (Device device = Device.Open()) + using (Record recording = Record.Create(@"output.mkv", device, configuration)) + { + device.StartCameras(configuration); + + recording.WriteHeader(); + + for (frame = 0; frame < 100; frame++) + { + using (Capture capture = device.GetCapture()) + { + recording.WriteCapture(capture); + } + } + } + + Console.WriteLine($"Wrote {frame} frames to output.mkv"); + } + } +} diff --git a/src/csharp/K4a.sln b/src/csharp/K4a.sln index a2cb9c758..88e889c61 100644 --- a/src/csharp/K4a.sln +++ b/src/csharp/K4a.sln @@ -40,86 +40,146 @@ 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("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Azure.Kinect.Sensor.Record", "Record\Microsoft.Azure.Kinect.Sensor.Record.csproj", "{71EAC57F-4023-4B45-8F9E-4A7C05A6BDB3}" +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|Any CPU + {CCD99E9D-1EE2-41F5-AD3F-4110A466A9A4}.Debug|Any CPU.Build.0 = Debug|Any CPU {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|Any CPU + {CCD99E9D-1EE2-41F5-AD3F-4110A466A9A4}.Release|Any CPU.Build.0 = Release|Any CPU {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|Any CPU + {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Debug|Any CPU.Build.0 = Debug|Any CPU + {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Debug|x64.ActiveCfg = Debug|Any CPU + {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Debug|x64.Build.0 = Debug|Any CPU + {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Debug|x86.ActiveCfg = Debug|Any CPU + {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Debug|x86.Build.0 = Debug|Any CPU + {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Release|Any CPU.ActiveCfg = Release|Any CPU + {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Release|Any CPU.Build.0 = Release|Any CPU + {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Release|x64.ActiveCfg = Release|Any CPU + {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Release|x64.Build.0 = Release|Any CPU + {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Release|x86.ActiveCfg = Release|Any CPU + {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Release|x86.Build.0 = Release|Any CPU + {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|Any CPU + {71EAC57F-4023-4B45-8F9E-4A7C05A6BDB3}.Debug|x64.Build.0 = Debug|Any CPU + {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 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -134,6 +194,7 @@ 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} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {9BC05C93-252F-4030-ACA6-41B4B54F9C86} diff --git a/src/csharp/Record/Exceptions/AzureKinectAddTagException.cs b/src/csharp/Record/Exceptions/AzureKinectAddTagException.cs new file mode 100644 index 000000000..1adaf22fd --- /dev/null +++ b/src/csharp/Record/Exceptions/AzureKinectAddTagException.cs @@ -0,0 +1,118 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +//------------------------------------------------------------------------------ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using Microsoft.Azure.Kinect.Sensor.Record.Exceptions; + +namespace Microsoft.Azure.Kinect.Sensor +{ + /// + /// 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..0f3b43457 --- /dev/null +++ b/src/csharp/Record/Exceptions/AzureKinectCreateRecordingException.cs @@ -0,0 +1,119 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +//------------------------------------------------------------------------------ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using Microsoft.Azure.Kinect.Sensor.Record.Exceptions; + +namespace Microsoft.Azure.Kinect.Sensor +{ + /// + /// Represents errors that occur when creating an Azure Kinect sensor recording. + /// + [Serializable] + public class AzureKinectCreateRecordingException : AzureKinectException + { + /// + /// 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. + /// + /// 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 AzureKinectCreateRecordingException($"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 AzureKinectCreateRecordingException($"result = {result}", tracer.LogMessages); + } + } + } +} diff --git a/src/csharp/Record/Exceptions/AzureKinectRecordException.cs b/src/csharp/Record/Exceptions/AzureKinectRecordException.cs new file mode 100644 index 000000000..5ba743eef --- /dev/null +++ b/src/csharp/Record/Exceptions/AzureKinectRecordException.cs @@ -0,0 +1,142 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Text; + +namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions +{ + [Serializable] + public abstract class AzureKinectRecordException : AzureKinectException + { + /// + /// Initializes a new instance of the class. + /// + public AzureKinectRecordException() + { + } + + /// + /// Initializes a new instance of the class + /// with a specified error message. + /// + /// The message that describes the error. + public 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. + /// + public 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/Microsoft.Azure.Kinect.Sensor.Record.csproj b/src/csharp/Record/Microsoft.Azure.Kinect.Sensor.Record.csproj new file mode 100644 index 000000000..298872f62 --- /dev/null +++ b/src/csharp/Record/Microsoft.Azure.Kinect.Sensor.Record.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/csharp/Record/NativeMethods.cs b/src/csharp/Record/NativeMethods.cs new file mode 100644 index 000000000..c1ae97e24 --- /dev/null +++ b/src/csharp/Record/NativeMethods.cs @@ -0,0 +1,379 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +//------------------------------------------------------------------------------ +using System; +using System.Globalization; +using System.Linq.Expressions; +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 + 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, + } + + public enum k4a_playback_seek_origin_t + { + K4A_PLAYBACK_SEEK_BEGIN = 0, + K4A_PLAYBACK_SEEK_END, + K4A_PLAYBACK_SEEK_DEVICE_TIME + } + + [DllImport("k4a", CallingConvention = k4aCallingConvention)] + public static extern k4a_result_t k4a_set_debug_message_handler( + k4a_logging_message_cb_t message_cb, + IntPtr message_cb_context, + LogLevel min_level); + + [DllImport("k4arecord", CallingConvention = k4aCallingConvention, CharSet = CharSet.Ansi)] + public static extern k4a_result_t k4a_record_create(string path, IntPtr device, k4a_device_configuration_t deviceConfiguration, out k4a_record_t handle); + + [DllImport("k4arecord", CallingConvention = k4aCallingConvention, CharSet = CharSet.Ansi)] + public static extern k4a_result_t k4a_record_add_tag(k4a_record_t handle, string name, string value); + + [DllImport("k4arecord", CallingConvention = k4aCallingConvention, CharSet = CharSet.Ansi)] + public static extern k4a_result_t k4a_record_add_imu_track(k4a_result_t handle); + + [DllImport("k4arecord", CallingConvention = k4aCallingConvention, CharSet = CharSet.Ansi)] + public static extern k4a_result_t k4a_record_add_attachment(k4a_result_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_result_t handle, string track_name, string codec_id, byte[] codec_context, UIntPtr codec_context_size, k4a_record_video_settings_t track_settings); + + [DllImport("k4arecord", CallingConvention = k4aCallingConvention, CharSet = CharSet.Ansi)] + public static extern k4a_result_t k4a_record_add_custom_subtitle_track(k4a_result_t handle, string track_name, string codec_id, byte[] codec_context, UIntPtr codec_context_size, k4a_record_subtitle_settings_t 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, UIntPtr data_size); + + [DllImport("k4arecord", CallingConvention = k4aCallingConvention)] + public static extern k4a_result_t k4a_playback_get_calibration(k4a_playback_t playback_handle, Calibration calibration); + + [DllImport("k4arecord", CallingConvention = k4aCallingConvention)] + public static extern k4a_result_t k4a_playback_get_record_configuration(k4a_playback_t playback_handle, out 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, out string track_name, 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 k4a_record_video_settings_t 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, out string codec_id, 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, + UIntPtr codec_context_size); + + [DllImport("k4arecord", CallingConvention = k4aCallingConvention)] + public static extern k4a_buffer_result_t k4a_playback_get_tag(k4a_playback_t playback_handle, + string track_name, + out string value, + 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)] + public static extern k4a_result_t k4a_playback_get_attachment(k4a_playback_t playback_handle, + string file_name, + out byte[] data, + 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 UInt64 k4a_playback_data_block_get_device_timestamp_usec(k4a_playback_data_block_t data_block_handle); + + [DllImport("k4arecord", CallingConvention = k4aCallingConvention)] + public static extern UInt64 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, UInt64 offset_usec, k4a_playback_seek_origin_t origin); + + [DllImport("k4arecord", CallingConvention = k4aCallingConvention)] + public static extern UInt64 k4a_playback_get_recording_length_usec(k4a_playback_t playback_handle); + + [DllImport("k4arecord", CallingConvention = k4aCallingConvention)] + public static extern UInt64 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, + }; + } + } + + 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), + }; + } + } + + [StructLayout(LayoutKind.Sequential)] + public class k4a_record_configuration_t + { + ImageFormat color_format; + ColorResolution color_resolution; + DepthMode depth_mode; + FPS camera_fps; + bool color_track_enabled; + bool depth_track_enabled; + bool imu_track_enabled; + int depth_delay_off_color_usec; + WiredSyncMode wired_sync_mode; + uint subordinate_delay_off_master_usec; + uint start_timestamp_offset_usec; + } + + [StructLayout(LayoutKind.Sequential)] + public class k4a_record_video_settings_t + { + UInt64 width; + UInt64 height; + UInt64 frame_rate; + } + + [StructLayout(LayoutKind.Sequential)] + public class k4a_record_subtitle_settings_t + { + bool high_freq_data; + } + } +#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/Record.cs b/src/csharp/Record/Record.cs new file mode 100644 index 000000000..e07c58cd8 --- /dev/null +++ b/src/csharp/Record/Record.cs @@ -0,0 +1,139 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +//------------------------------------------------------------------------------ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.Azure.Kinect.Sensor.Record +{ + /// + /// Represents a writable sensor recording. + /// + public class Record : IDisposable + { + // The native handle for this recording. + private NativeMethods.k4a_record_t handle; + + // To detect redundant calls to Dispose + private bool disposedValue = false; + + private Record(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 Record Create(string path, Device device, DeviceConfiguration deviceConfiguration) + { + NativeMethods.k4a_record_t handle = null; + 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(() => NativeMethods.k4a_record_create(path, device.Handle, NativeMethods.k4a_device_configuration_t.FromDeviceConfiguration(deviceConfiguration), out handle)); + } + } + else + { + AzureKinectCreateRecordingException.ThrowIfNotSuccess(() => NativeMethods.k4a_record_create(path, IntPtr.Zero, NativeMethods.k4a_device_configuration_t.FromDeviceConfiguration(deviceConfiguration), out handle)); + } + + return new Record(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(Record)); + } + + AzureKinectAddTagException.ThrowIfNotSuccess(() => NativeMethods.k4a_record_add_tag(this.handle, name, value)); + } + } + + /// + /// Writes the recording header to disk. + /// + public void WriteHeader() + { + lock (this) + { + if (this.disposedValue) + { + throw new ObjectDisposedException(nameof(Record)); + } + + // TODO: Change exception type + AzureKinectAddTagException.ThrowIfNotSuccess(() => NativeMethods.k4a_record_write_header(this.handle)); + } + } + + /// + /// Writes a capture to the recording file. + /// + /// Capture containing data to write. + public void WriteCapture(Capture capture) + { + lock (this) + { + if (this.disposedValue) + { + throw new ObjectDisposedException(nameof(Record)); + } + + if (capture == null) + { + throw new ArgumentNullException(nameof(capture)); + } + + using (Capture reference = capture.Reference()) + { + // TODO: Change exception type + AzureKinectAddTagException.ThrowIfNotSuccess(() => NativeMethods.k4a_record_write_capture(this.handle, reference.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/Capture.cs b/src/csharp/SDK/Capture.cs index b69844efb..c3d881c81 100644 --- a/src/csharp/SDK/Capture.cs +++ b/src/csharp/SDK/Capture.cs @@ -169,6 +169,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..263e3b88c 100644 --- a/src/csharp/SDK/Device.cs +++ b/src/csharp/SDK/Device.cs @@ -167,6 +167,53 @@ 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 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; + } + } + /// /// Gets the number of currently connected devices. /// @@ -513,12 +560,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/Native/LoggingTracer.cs b/src/csharp/SDK/Native/LoggingTracer.cs index 265ab604c..5c7a230b4 100644 --- a/src/csharp/SDK/Native/LoggingTracer.cs +++ b/src/csharp/SDK/Native/LoggingTracer.cs @@ -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 5ea3ec5de..55f36de55 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; @@ -370,6 +372,26 @@ public static extern k4a_result_t k4a_set_debug_message_handler( IntPtr message_cb_context, LogLevel min_level); + [DllImport("k4arecord", CallingConvention = k4aCallingConvention, CharSet = CharSet.Ansi)] + [NativeReference] + public static extern k4a_result_t k4a_record_create(string path, k4a_device_t device, k4a_device_configuration_t deviceConfiguration, out k4a_record_t handle); + + [DllImport("k4arecord", CallingConvention = k4aCallingConvention)] + [NativeReference] + public static extern void k4a_record_close(IntPtr handle); + + [DllImport("k4arecord", CallingConvention = k4aCallingConvention, CharSet = CharSet.Ansi)] + [NativeReference] + public static extern k4a_result_t k4a_record_add_tag(k4a_record_t handle, string name, string value); + + [DllImport("k4arecord", CallingConvention = k4aCallingConvention)] + [NativeReference] + public static extern k4a_result_t k4a_record_write_header(k4a_record_t handle); + + [DllImport("k4arecord", CallingConvention = k4aCallingConvention)] + [NativeReference] + public static extern k4a_result_t k4a_record_write_capture(k4a_record_t handle, k4a_capture_t capture); + [NativeReference] [StructLayout(LayoutKind.Sequential)] public struct k4a_version_t @@ -502,6 +524,20 @@ protected override bool ReleaseHandle() } } + 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; + } + } + [StructLayout(LayoutKind.Sequential)] [Native.NativeReference("k4a_imu_sample_t")] public class k4a_imu_sample_t From 3f2241e309df56e6027472eda6ffeb8ae9517ee0 Mon Sep 17 00:00:00 2001 From: Brent Allen Date: Thu, 3 Oct 2019 15:40:31 -0700 Subject: [PATCH 02/16] Added record methods --- src/csharp/K4a.sln | 42 +--- .../AzureKinectAddAttachmentException.cs | 97 ++++++++ ...reKinectAddCustomSubtitleTrackException.cs | 97 ++++++++ ...AzureKinectAddCustomVideoTrackException.cs | 97 ++++++++ .../AzureKinectAddImuTrackException.cs | 97 ++++++++ .../Exceptions/AzureKinectFlushException.cs | 97 ++++++++ .../AzureKinectWriteCaptureException.cs | 97 ++++++++ ...zureKinectWriteCustomTrackDataException.cs | 97 ++++++++ .../AzureKinectWriteHeaderException.cs | 97 ++++++++ .../AzureKinectWriteImuSampleException.cs | 97 ++++++++ src/csharp/Record/NativeMethods.cs | 25 +- src/csharp/Record/Record.cs | 219 +++++++++++++++++- src/csharp/Record/RecordConfiguration.cs | 22 ++ src/csharp/Record/RecordSubtitleSettings.cs | 13 ++ src/csharp/Record/RecordVideoSettings.cs | 15 ++ 15 files changed, 1144 insertions(+), 65 deletions(-) create mode 100644 src/csharp/Record/Exceptions/AzureKinectAddAttachmentException.cs create mode 100644 src/csharp/Record/Exceptions/AzureKinectAddCustomSubtitleTrackException.cs create mode 100644 src/csharp/Record/Exceptions/AzureKinectAddCustomVideoTrackException.cs create mode 100644 src/csharp/Record/Exceptions/AzureKinectAddImuTrackException.cs create mode 100644 src/csharp/Record/Exceptions/AzureKinectFlushException.cs create mode 100644 src/csharp/Record/Exceptions/AzureKinectWriteCaptureException.cs create mode 100644 src/csharp/Record/Exceptions/AzureKinectWriteCustomTrackDataException.cs create mode 100644 src/csharp/Record/Exceptions/AzureKinectWriteHeaderException.cs create mode 100644 src/csharp/Record/Exceptions/AzureKinectWriteImuSampleException.cs create mode 100644 src/csharp/Record/RecordConfiguration.cs create mode 100644 src/csharp/Record/RecordSubtitleSettings.cs create mode 100644 src/csharp/Record/RecordVideoSettings.cs diff --git a/src/csharp/K4a.sln b/src/csharp/K4a.sln index 88e889c61..9904bdad9 100644 --- a/src/csharp/K4a.sln +++ b/src/csharp/K4a.sln @@ -42,140 +42,100 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Extensions", "Extensions", 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("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Azure.Kinect.Sensor.Record", "Record\Microsoft.Azure.Kinect.Sensor.Record.csproj", "{71EAC57F-4023-4B45-8F9E-4A7C05A6BDB3}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Azure.Kinect.Sensor.Record", "Record\Microsoft.Azure.Kinect.Sensor.Record.csproj", "{71EAC57F-4023-4B45-8F9E-4A7C05A6BDB3}" 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|Any CPU - {CCD99E9D-1EE2-41F5-AD3F-4110A466A9A4}.Debug|Any CPU.Build.0 = Debug|Any CPU {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|Any CPU - {CCD99E9D-1EE2-41F5-AD3F-4110A466A9A4}.Release|Any CPU.Build.0 = Release|Any CPU {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|Any CPU - {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Debug|Any CPU.Build.0 = Debug|Any CPU {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Debug|x64.ActiveCfg = Debug|Any CPU {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Debug|x64.Build.0 = Debug|Any CPU {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Debug|x86.ActiveCfg = Debug|Any CPU {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Debug|x86.Build.0 = Debug|Any CPU - {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Release|Any CPU.ActiveCfg = Release|Any CPU - {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Release|Any CPU.Build.0 = Release|Any CPU {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Release|x64.ActiveCfg = Release|Any CPU {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Release|x64.Build.0 = Release|Any CPU {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Release|x86.ActiveCfg = Release|Any CPU {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Release|x86.Build.0 = Release|Any CPU - {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|Any CPU {71EAC57F-4023-4B45-8F9E-4A7C05A6BDB3}.Debug|x64.Build.0 = Debug|Any CPU {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 diff --git a/src/csharp/Record/Exceptions/AzureKinectAddAttachmentException.cs b/src/csharp/Record/Exceptions/AzureKinectAddAttachmentException.cs new file mode 100644 index 000000000..c18021deb --- /dev/null +++ b/src/csharp/Record/Exceptions/AzureKinectAddAttachmentException.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Text; + +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..b967cf532 --- /dev/null +++ b/src/csharp/Record/Exceptions/AzureKinectAddCustomSubtitleTrackException.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Text; + +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..e07ceab5f --- /dev/null +++ b/src/csharp/Record/Exceptions/AzureKinectAddCustomVideoTrackException.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Text; + +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..bb0f9b2f8 --- /dev/null +++ b/src/csharp/Record/Exceptions/AzureKinectAddImuTrackException.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Text; + +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/AzureKinectFlushException.cs b/src/csharp/Record/Exceptions/AzureKinectFlushException.cs new file mode 100644 index 000000000..6b24c2c8a --- /dev/null +++ b/src/csharp/Record/Exceptions/AzureKinectFlushException.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Text; + +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/AzureKinectWriteCaptureException.cs b/src/csharp/Record/Exceptions/AzureKinectWriteCaptureException.cs new file mode 100644 index 000000000..15d5c3f7c --- /dev/null +++ b/src/csharp/Record/Exceptions/AzureKinectWriteCaptureException.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Text; + +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..2358083b1 --- /dev/null +++ b/src/csharp/Record/Exceptions/AzureKinectWriteCustomTrackDataException.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Text; + +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..23e97e83b --- /dev/null +++ b/src/csharp/Record/Exceptions/AzureKinectWriteHeaderException.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Text; + +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..9b031f2fb --- /dev/null +++ b/src/csharp/Record/Exceptions/AzureKinectWriteImuSampleException.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Text; + +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/NativeMethods.cs b/src/csharp/Record/NativeMethods.cs index c1ae97e24..05012bd33 100644 --- a/src/csharp/Record/NativeMethods.cs +++ b/src/csharp/Record/NativeMethods.cs @@ -70,16 +70,16 @@ public static extern k4a_result_t k4a_set_debug_message_handler( 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_result_t handle); + 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_result_t handle, string attachment_name, byte[] buffer, UIntPtr buffer_size); + 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_result_t handle, string track_name, string codec_id, byte[] codec_context, UIntPtr codec_context_size, k4a_record_video_settings_t track_settings); + 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_result_t handle, string track_name, string codec_id, byte[] codec_context, UIntPtr codec_context_size, k4a_record_subtitle_settings_t track_settings); + 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); @@ -123,8 +123,8 @@ public static extern k4a_result_t k4a_set_debug_message_handler( [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 k4a_record_video_settings_t video_settings); + //[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 k4a_record_video_settings_t 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, out string codec_id, UIntPtr codec_id_size); @@ -359,19 +359,6 @@ public class k4a_record_configuration_t uint start_timestamp_offset_usec; } - [StructLayout(LayoutKind.Sequential)] - public class k4a_record_video_settings_t - { - UInt64 width; - UInt64 height; - UInt64 frame_rate; - } - - [StructLayout(LayoutKind.Sequential)] - public class k4a_record_subtitle_settings_t - { - bool high_freq_data; - } } #pragma warning restore SA1602 // Enumeration items should be documented #pragma warning restore SA1600 // Elements should be documented diff --git a/src/csharp/Record/Record.cs b/src/csharp/Record/Record.cs index e07c58cd8..c0aa9c210 100644 --- a/src/csharp/Record/Record.cs +++ b/src/csharp/Record/Record.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Text; +using Microsoft.Azure.Kinect.Sensor.Record.Exceptions; namespace Microsoft.Azure.Kinect.Sensor.Record { @@ -72,8 +73,127 @@ public void AddTag(string name, string value) } /// - /// Writes the recording header to disk. + /// Adds an IMU track to the recording. /// + public void AddImuTrack() + { + lock (this) + { + if (this.disposedValue) + { + throw new ObjectDisposedException(nameof(Record)); + } + + 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(Record)); + } + + 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(Record)); + } + + 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(Record)); + } + + 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) @@ -83,8 +203,7 @@ public void WriteHeader() throw new ObjectDisposedException(nameof(Record)); } - // TODO: Change exception type - AzureKinectAddTagException.ThrowIfNotSuccess(() => NativeMethods.k4a_record_write_header(this.handle)); + AzureKinectWriteHeaderException.ThrowIfNotSuccess(() => NativeMethods.k4a_record_write_header(this.handle)); } } @@ -92,6 +211,12 @@ public void WriteHeader() /// 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) @@ -108,12 +233,96 @@ public void WriteCapture(Capture capture) using (Capture reference = capture.Reference()) { - // TODO: Change exception type - AzureKinectAddTagException.ThrowIfNotSuccess(() => NativeMethods.k4a_record_write_capture(this.handle, reference.Handle)); + AzureKinectWriteCaptureException.ThrowIfNotSuccess(() => NativeMethods.k4a_record_write_capture(this.handle, reference.Handle)); } } } + public void WriteImuSample(ImuSample imuSample) + { + lock (this) + { + if (this.disposedValue) + { + throw new ObjectDisposedException(nameof(Record)); + } + + 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(Record)); + } + + 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(Record)); + } + + AzureKinectFlushException.ThrowIfNotSuccess(() => NativeMethods.k4a_record_flush(this.handle)); + } + } + /// public void Dispose() { diff --git a/src/csharp/Record/RecordConfiguration.cs b/src/csharp/Record/RecordConfiguration.cs new file mode 100644 index 000000000..0feca0df1 --- /dev/null +++ b/src/csharp/Record/RecordConfiguration.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.Azure.Kinect.Sensor.Record +{ + public class RecordConfiguration + { + ImageFormat ColorFormat { get; set; } + ColorResolution ColorResolution { get; set; } + DepthMode DepthMode { get; set; } + FPS CameraFPS { get; set; } + bool ColorTrackEnabled { get; set; } + bool DepthTrackEnabled { get; set; } + bool IMUTrackEnabled { get; set; } + TimeSpan DepthDelayOffColor { get; set; } + WiredSyncMode WiredSyncMode { get; set; } + TimeSpan SubordinateDelayOffMaster { get; set; } + TimeSpan StartTimestampOffset { get; set; } + + } +} diff --git a/src/csharp/Record/RecordSubtitleSettings.cs b/src/csharp/Record/RecordSubtitleSettings.cs new file mode 100644 index 000000000..7b67582e9 --- /dev/null +++ b/src/csharp/Record/RecordSubtitleSettings.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; + +namespace Microsoft.Azure.Kinect.Sensor.Record +{ + [StructLayout(LayoutKind.Sequential)] + public class RecordSubtitleSettings + { + bool HighFrequencyData { get; set; } + } +} diff --git a/src/csharp/Record/RecordVideoSettings.cs b/src/csharp/Record/RecordVideoSettings.cs new file mode 100644 index 000000000..0ebb9a02c --- /dev/null +++ b/src/csharp/Record/RecordVideoSettings.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; + +namespace Microsoft.Azure.Kinect.Sensor.Record +{ + [StructLayout(LayoutKind.Sequential)] + public class RecordVideoSettings + { + public ulong Width { get; set; } + public ulong Height { get; set; } + public ulong FrameRate { get; set; } + } +} From 6d1a5889096b84ab9951141a25edbdccf752f667 Mon Sep 17 00:00:00 2001 From: Brent Allen Date: Fri, 4 Oct 2019 09:27:01 -0700 Subject: [PATCH 03/16] Added minmial playback APIs --- src/csharp/Examples/Recording/Program.cs | 19 + .../AzureKinectGetCalibrationException.cs | 97 ++++ .../AzureKinectGetCaptureException.cs | 97 ++++ .../AzureKinectGetRawCalibrationException.cs | 97 ++++ .../Exceptions/AzureKinectGetTagException.cs | 97 ++++ ...zureKinectGetTrackCodecContextException.cs | 97 ++++ .../AzureKinectGetTrackNameException.cs | 97 ++++ .../AzureKinectOpenPlaybackException.cs | 97 ++++ .../AzureKinectSetColorConversionException.cs | 97 ++++ ...ureKinectTrackGetVideoSettingsException.cs | 97 ++++ src/csharp/Record/NativeMethods.cs | 46 +- src/csharp/Record/Playback.cs | 437 ++++++++++++++++++ src/csharp/Record/Record.cs | 2 +- src/csharp/Record/RecordConfiguration.cs | 39 +- src/csharp/Record/RecordSubtitleSettings.cs | 2 +- src/csharp/SDK/Capture.cs | 12 + src/csharp/SDK/Native/NativeMethods.cs | 6 + 17 files changed, 1400 insertions(+), 36 deletions(-) create mode 100644 src/csharp/Record/Exceptions/AzureKinectGetCalibrationException.cs create mode 100644 src/csharp/Record/Exceptions/AzureKinectGetCaptureException.cs create mode 100644 src/csharp/Record/Exceptions/AzureKinectGetRawCalibrationException.cs create mode 100644 src/csharp/Record/Exceptions/AzureKinectGetTagException.cs create mode 100644 src/csharp/Record/Exceptions/AzureKinectGetTrackCodecContextException.cs create mode 100644 src/csharp/Record/Exceptions/AzureKinectGetTrackNameException.cs create mode 100644 src/csharp/Record/Exceptions/AzureKinectOpenPlaybackException.cs create mode 100644 src/csharp/Record/Exceptions/AzureKinectSetColorConversionException.cs create mode 100644 src/csharp/Record/Exceptions/AzureKinectTrackGetVideoSettingsException.cs create mode 100644 src/csharp/Record/Playback.cs diff --git a/src/csharp/Examples/Recording/Program.cs b/src/csharp/Examples/Recording/Program.cs index 647d0b803..959c65fe0 100644 --- a/src/csharp/Examples/Recording/Program.cs +++ b/src/csharp/Examples/Recording/Program.cs @@ -37,6 +37,25 @@ static void Main(string[] args) } Console.WriteLine($"Wrote {frame} frames to output.mkv"); + + using (Playback playback = Playback.Open(@"output.mkv")) + { + Console.WriteLine($"Tracks = {playback.TrackCount}"); + + 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($"Depth timestamp: {capture.Depth.DeviceTimestamp}"); + + } + } } } } diff --git a/src/csharp/Record/Exceptions/AzureKinectGetCalibrationException.cs b/src/csharp/Record/Exceptions/AzureKinectGetCalibrationException.cs new file mode 100644 index 000000000..dbeb25d72 --- /dev/null +++ b/src/csharp/Record/Exceptions/AzureKinectGetCalibrationException.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Text; + +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..f1d08d2c5 --- /dev/null +++ b/src/csharp/Record/Exceptions/AzureKinectGetCaptureException.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Text; + +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/AzureKinectGetRawCalibrationException.cs b/src/csharp/Record/Exceptions/AzureKinectGetRawCalibrationException.cs new file mode 100644 index 000000000..7798495c7 --- /dev/null +++ b/src/csharp/Record/Exceptions/AzureKinectGetRawCalibrationException.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Text; + +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..dd1627cc2 --- /dev/null +++ b/src/csharp/Record/Exceptions/AzureKinectGetTagException.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Text; + +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); + } + } + } + } +} \ 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..3a2f47ab1 --- /dev/null +++ b/src/csharp/Record/Exceptions/AzureKinectGetTrackCodecContextException.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Text; + +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..18543fdda --- /dev/null +++ b/src/csharp/Record/Exceptions/AzureKinectGetTrackNameException.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Text; + +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..4ab1a3b10 --- /dev/null +++ b/src/csharp/Record/Exceptions/AzureKinectOpenPlaybackException.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Text; + +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/AzureKinectSetColorConversionException.cs b/src/csharp/Record/Exceptions/AzureKinectSetColorConversionException.cs new file mode 100644 index 000000000..a2c9d3f32 --- /dev/null +++ b/src/csharp/Record/Exceptions/AzureKinectSetColorConversionException.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Text; + +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..a5fc9363a --- /dev/null +++ b/src/csharp/Record/Exceptions/AzureKinectTrackGetVideoSettingsException.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Text; + +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/NativeMethods.cs b/src/csharp/Record/NativeMethods.cs index 05012bd33..1442b1ebf 100644 --- a/src/csharp/Record/NativeMethods.cs +++ b/src/csharp/Record/NativeMethods.cs @@ -103,10 +103,10 @@ public static extern k4a_result_t k4a_set_debug_message_handler( 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, UIntPtr data_size); + 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, Calibration calibration); + 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, out k4a_record_configuration_t configuration); @@ -118,38 +118,38 @@ public static extern k4a_result_t k4a_set_debug_message_handler( 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, out string track_name, UIntPtr track_name_size); + 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 k4a_record_video_settings_t video_settings); + [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, out string codec_id, UIntPtr codec_id_size); + 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, - UIntPtr codec_context_size); + ref UIntPtr codec_context_size); [DllImport("k4arecord", CallingConvention = k4aCallingConvention)] public static extern k4a_buffer_result_t k4a_playback_get_tag(k4a_playback_t playback_handle, string track_name, - out string value, - UIntPtr codec_context_size); + 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)] - public static extern k4a_result_t k4a_playback_get_attachment(k4a_playback_t playback_handle, + public static extern k4a_buffer_result_t k4a_playback_get_attachment(k4a_playback_t playback_handle, string file_name, - out byte[] data, - UIntPtr data_size); + 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, @@ -346,17 +346,17 @@ public ImuSample ToImuSample() [StructLayout(LayoutKind.Sequential)] public class k4a_record_configuration_t { - ImageFormat color_format; - ColorResolution color_resolution; - DepthMode depth_mode; - FPS camera_fps; - bool color_track_enabled; - bool depth_track_enabled; - bool imu_track_enabled; - int depth_delay_off_color_usec; - WiredSyncMode wired_sync_mode; - uint subordinate_delay_off_master_usec; - uint start_timestamp_offset_usec; + public ImageFormat color_format; + public ColorResolution color_resolution; + public DepthMode depth_mode; + public FPS camera_fps; + public bool color_track_enabled; + public bool depth_track_enabled; + public bool 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; } } diff --git a/src/csharp/Record/Playback.cs b/src/csharp/Record/Playback.cs new file mode 100644 index 000000000..946103aea --- /dev/null +++ b/src/csharp/Record/Playback.cs @@ -0,0 +1,437 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.Azure.Kinect.Sensor.Record.Exceptions; + +namespace Microsoft.Azure.Kinect.Sensor.Record +{ + 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; + } + + /// + /// Opens an existing recording file for reading. + /// + /// Filesystem path of the existing recording. + /// + 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; + } + } + + /// + /// 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) + { + Calibration localCalibration = new Calibration(); + if (NativeMethods.k4a_result_t.K4A_RESULT_SUCCEEDED == NativeMethods.k4a_playback_get_calibration(this.handle, out localCalibration)) + { + this.calibration = localCalibration; + } + } + + return this.calibration; + } + } + } + + public RecordConfiguration RecordConfiguration + { + get + { + lock (this) + { + if (this.disposedValue) + { + throw new ObjectDisposedException(nameof(Playback)); + } + + if (this.recordConfiguration == null) + { + if (NativeMethods.k4a_result_t.K4A_RESULT_SUCCEEDED == NativeMethods.k4a_playback_get_record_configuration(this.handle, out NativeMethods.k4a_record_configuration_t nativeConfig)) + { + this.recordConfiguration = RecordConfiguration.FromNative(nativeConfig); + } + } + + return this.recordConfiguration; + } + } + } + + 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); + } + } + 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)); + } + } + } + + 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(); + } + } + + 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); + } + } + + 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; + } + } + + 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(); + } + } + + 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; + } + } + + 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); + if (NativeMethods.k4a_playback_get_tag(this.handle, name, 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 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(); + } + } + + 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)); + } + } + + 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; + } + } + + 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(captureHandle); + } + + return 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(captureHandle); + } + + return null; + } + } + + /// + 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/Record.cs b/src/csharp/Record/Record.cs index c0aa9c210..3b82f13d6 100644 --- a/src/csharp/Record/Record.cs +++ b/src/csharp/Record/Record.cs @@ -17,7 +17,7 @@ namespace Microsoft.Azure.Kinect.Sensor.Record public class Record : IDisposable { // The native handle for this recording. - private NativeMethods.k4a_record_t handle; + private readonly NativeMethods.k4a_record_t handle; // To detect redundant calls to Dispose private bool disposedValue = false; diff --git a/src/csharp/Record/RecordConfiguration.cs b/src/csharp/Record/RecordConfiguration.cs index 0feca0df1..995ad34f0 100644 --- a/src/csharp/Record/RecordConfiguration.cs +++ b/src/csharp/Record/RecordConfiguration.cs @@ -6,17 +6,34 @@ namespace Microsoft.Azure.Kinect.Sensor.Record { public class RecordConfiguration { - ImageFormat ColorFormat { get; set; } - ColorResolution ColorResolution { get; set; } - DepthMode DepthMode { get; set; } - FPS CameraFPS { get; set; } - bool ColorTrackEnabled { get; set; } - bool DepthTrackEnabled { get; set; } - bool IMUTrackEnabled { get; set; } - TimeSpan DepthDelayOffColor { get; set; } - WiredSyncMode WiredSyncMode { get; set; } - TimeSpan SubordinateDelayOffMaster { get; set; } - TimeSpan StartTimestampOffset { get; set; } + public ImageFormat ColorFormat { get; set; } + public ColorResolution ColorResolution { get; set; } + public DepthMode DepthMode { get; set; } + public FPS CameraFPS { get; set; } + public bool ColorTrackEnabled { get; set; } + public bool DepthTrackEnabled { get; set; } + public bool IMUTrackEnabled { get; set; } + public TimeSpan DepthDelayOffColor { get; set; } + public WiredSyncMode WiredSyncMode { get; set; } + public TimeSpan SubordinateDelayOffMaster { get; set; } + public TimeSpan StartTimestampOffset { get; set; } + 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, + DepthTrackEnabled = config.depth_track_enabled, + IMUTrackEnabled = config.imu_track_enabled, + 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 index 7b67582e9..f66c08f57 100644 --- a/src/csharp/Record/RecordSubtitleSettings.cs +++ b/src/csharp/Record/RecordSubtitleSettings.cs @@ -8,6 +8,6 @@ namespace Microsoft.Azure.Kinect.Sensor.Record [StructLayout(LayoutKind.Sequential)] public class RecordSubtitleSettings { - bool HighFrequencyData { get; set; } + public bool HighFrequencyData { get; set; } } } diff --git a/src/csharp/SDK/Capture.cs b/src/csharp/SDK/Capture.cs index c3d881c81..7835ec844 100644 --- a/src/csharp/SDK/Capture.cs +++ b/src/csharp/SDK/Capture.cs @@ -37,6 +37,18 @@ public Capture() Allocator.Singleton.RegisterForDisposal(this); } + /// + /// Initializes a new instance of the class from an existing native handle. + /// + /// Native handle to a capture. + /// + /// This constructor will take ownership of the existing handle. Disposing this Capture will close the handle. + /// + public Capture(IntPtr handle) + : this(new NativeMethods.k4a_capture_t(handle)) + { + } + /// /// Initializes a new instance of the class. /// diff --git a/src/csharp/SDK/Native/NativeMethods.cs b/src/csharp/SDK/Native/NativeMethods.cs index 55f36de55..e8e8a9a41 100644 --- a/src/csharp/SDK/Native/NativeMethods.cs +++ b/src/csharp/SDK/Native/NativeMethods.cs @@ -462,6 +462,12 @@ protected override bool ReleaseHandle() public class k4a_capture_t : Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid { + public k4a_capture_t(IntPtr handle) + : base(true) + { + this.handle = handle; + } + private k4a_capture_t() : base(true) { From 7209221ed3d1492068e7b7456c950d1a41548fe7 Mon Sep 17 00:00:00 2001 From: Brent Allen Date: Fri, 4 Oct 2019 11:24:56 -0700 Subject: [PATCH 04/16] Completed Playback API --- src/csharp/Examples/Recording/Program.cs | 31 +++++- src/csharp/Record/DataBlock.cs | 99 +++++++++++++++++++ .../AzureKinectGetDataBlockException.cs | 97 ++++++++++++++++++ .../Exceptions/AzureKinectSeekException.cs | 97 ++++++++++++++++++ src/csharp/Record/NativeMethods.cs | 12 +-- src/csharp/Record/Playback.cs | 97 ++++++++++++++++++ src/csharp/Record/PlaybackSeekOrigin.cs | 13 +++ 7 files changed, 433 insertions(+), 13 deletions(-) create mode 100644 src/csharp/Record/DataBlock.cs create mode 100644 src/csharp/Record/Exceptions/AzureKinectGetDataBlockException.cs create mode 100644 src/csharp/Record/Exceptions/AzureKinectSeekException.cs create mode 100644 src/csharp/Record/PlaybackSeekOrigin.cs diff --git a/src/csharp/Examples/Recording/Program.cs b/src/csharp/Examples/Recording/Program.cs index 959c65fe0..6d9df61b2 100644 --- a/src/csharp/Examples/Recording/Program.cs +++ b/src/csharp/Examples/Recording/Program.cs @@ -1,4 +1,5 @@ using System; +using System.Linq.Expressions; using Microsoft.Azure.Kinect.Sensor; using Microsoft.Azure.Kinect.Sensor.Record; @@ -17,14 +18,18 @@ static void Main(string[] args) CameraFPS = FPS.FPS30, ColorFormat = ImageFormat.ColorMJPG, ColorResolution = ColorResolution.R720p, - DepthMode = DepthMode.NFOV_2x2Binned + DepthMode = DepthMode.NFOV_2x2Binned, + SynchronizedImagesOnly = true }; using (Device device = Device.Open()) using (Record recording = Record.Create(@"output.mkv", device, configuration)) { + device.StartCameras(configuration); + device.StartImu(); + recording.AddImuTrack(); recording.WriteHeader(); for (frame = 0; frame < 100; frame++) @@ -32,6 +37,21 @@ static void Main(string[] args) using (Capture capture = device.GetCapture()) { recording.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); + + recording.WriteImuSample(sample); + Console.WriteLine($"Wrote imu ({sample.AccelerometerTimestamp})"); + } + }catch (TimeoutException) + { + + } } } } @@ -41,19 +61,20 @@ static void Main(string[] args) using (Playback playback = Playback.Open(@"output.mkv")) { Console.WriteLine($"Tracks = {playback.TrackCount}"); - + Console.WriteLine($"RecordingLength = {playback.RecordingLength}"); + Console.WriteLine($"LastTimestamp = {playback.LastTimestamp}"); + 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)}"); + Console.WriteLine($" Track {i}: {name} ({codecId}) (builtin={playback.GetTrackIsBuiltin(name)})"); } Capture capture; while ( null != (capture = playback.GetNextCapture())) { - Console.WriteLine($"Depth timestamp: {capture.Depth.DeviceTimestamp}"); - + Console.WriteLine($"Color timestamp: {capture.Color.DeviceTimestamp} Depth timestamp: {capture.Depth.DeviceTimestamp}"); } } } diff --git a/src/csharp/Record/DataBlock.cs b/src/csharp/Record/DataBlock.cs new file mode 100644 index 000000000..5fdc0a52c --- /dev/null +++ b/src/csharp/Record/DataBlock.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Text; + +namespace Microsoft.Azure.Kinect.Sensor.Record +{ + public class DataBlock : IDisposable + { + // 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; + + internal DataBlock(NativeMethods.k4a_playback_data_block_t handle) + { + this.handle = handle; + } + + public byte[] Buffer + { + 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; + } + } + } + + 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/AzureKinectGetDataBlockException.cs b/src/csharp/Record/Exceptions/AzureKinectGetDataBlockException.cs new file mode 100644 index 000000000..4180488fc --- /dev/null +++ b/src/csharp/Record/Exceptions/AzureKinectGetDataBlockException.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Text; + +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/AzureKinectSeekException.cs b/src/csharp/Record/Exceptions/AzureKinectSeekException.cs new file mode 100644 index 000000000..20f0213c4 --- /dev/null +++ b/src/csharp/Record/Exceptions/AzureKinectSeekException.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Text; + +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/NativeMethods.cs b/src/csharp/Record/NativeMethods.cs index 1442b1ebf..36de967d0 100644 --- a/src/csharp/Record/NativeMethods.cs +++ b/src/csharp/Record/NativeMethods.cs @@ -50,13 +50,6 @@ public enum k4a_stream_result_t K4A_STREAM_RESULT_EOF, } - public enum k4a_playback_seek_origin_t - { - K4A_PLAYBACK_SEEK_BEGIN = 0, - K4A_PLAYBACK_SEEK_END, - K4A_PLAYBACK_SEEK_DEVICE_TIME - } - [DllImport("k4a", CallingConvention = k4aCallingConvention)] public static extern k4a_result_t k4a_set_debug_message_handler( k4a_logging_message_cb_t message_cb, @@ -178,6 +171,9 @@ public static extern k4a_stream_result_t k4a_playback_get_previous_data_block(k4 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 UInt64 k4a_playback_data_block_get_device_timestamp_usec(k4a_playback_data_block_t data_block_handle); @@ -189,7 +185,7 @@ public static extern k4a_stream_result_t k4a_playback_get_previous_data_block(k4 [DllImport("k4arecord", CallingConvention = k4aCallingConvention)] - public static extern k4a_result_t k4a_playback_seek_timestamp(k4a_playback_t playback_handle, UInt64 offset_usec, k4a_playback_seek_origin_t origin); + public static extern k4a_result_t k4a_playback_seek_timestamp(k4a_playback_t playback_handle, UInt64 offset_usec, PlaybackSeekOrigin origin); [DllImport("k4arecord", CallingConvention = k4aCallingConvention)] public static extern UInt64 k4a_playback_get_recording_length_usec(k4a_playback_t playback_handle); diff --git a/src/csharp/Record/Playback.cs b/src/csharp/Record/Playback.cs index 946103aea..baed8b95a 100644 --- a/src/csharp/Record/Playback.cs +++ b/src/csharp/Record/Playback.cs @@ -412,6 +412,103 @@ public Capture GetPreviousCapture() } } + 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; + } + } + + 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; + } + } + + 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 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); + } + } + + public TimeSpan LastTimestamp + { + get + { + if (this.disposedValue) + { + throw new ObjectDisposedException(nameof(Playback)); + } + + long length = checked((long)NativeMethods.k4a_playback_get_last_timestamp_usec(this.handle)); + return TimeSpan.FromTicks(length * 10); + } + } + /// public void Dispose() { diff --git a/src/csharp/Record/PlaybackSeekOrigin.cs b/src/csharp/Record/PlaybackSeekOrigin.cs new file mode 100644 index 000000000..d461cf137 --- /dev/null +++ b/src/csharp/Record/PlaybackSeekOrigin.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.Azure.Kinect.Sensor.Record +{ + public enum PlaybackSeekOrigin + { + Begin = 0, + End, + DeviceTime + } +} From 310287a8ab3ac0b39ff400a791f7e2d7a1986dac Mon Sep 17 00:00:00 2001 From: Brent Allen Date: Fri, 4 Oct 2019 14:43:56 -0700 Subject: [PATCH 05/16] Added documentation --- include/k4arecord/playback.h | 2 +- src/csharp/Examples/Recording/Program.cs | 1 - .../AzureKinectGetImuSampleException.cs | 97 +++++++ src/csharp/Record/Playback.cs | 259 ++++++++++++++++-- 4 files changed, 340 insertions(+), 19 deletions(-) create mode 100644 src/csharp/Record/Exceptions/AzureKinectGetImuSampleException.cs 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/Program.cs b/src/csharp/Examples/Recording/Program.cs index 6d9df61b2..077b7e5a3 100644 --- a/src/csharp/Examples/Recording/Program.cs +++ b/src/csharp/Examples/Recording/Program.cs @@ -62,7 +62,6 @@ static void Main(string[] args) { Console.WriteLine($"Tracks = {playback.TrackCount}"); Console.WriteLine($"RecordingLength = {playback.RecordingLength}"); - Console.WriteLine($"LastTimestamp = {playback.LastTimestamp}"); for (int i = 0; i < playback.TrackCount; i++) { diff --git a/src/csharp/Record/Exceptions/AzureKinectGetImuSampleException.cs b/src/csharp/Record/Exceptions/AzureKinectGetImuSampleException.cs new file mode 100644 index 000000000..d58523c3d --- /dev/null +++ b/src/csharp/Record/Exceptions/AzureKinectGetImuSampleException.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Text; + +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/Playback.cs b/src/csharp/Record/Playback.cs index baed8b95a..b18d65aea 100644 --- a/src/csharp/Record/Playback.cs +++ b/src/csharp/Record/Playback.cs @@ -96,7 +96,10 @@ public Calibration? Calibration } } } - + + /// + /// Get the device configuration used during recording. + /// public RecordConfiguration RecordConfiguration { get @@ -121,6 +124,11 @@ public RecordConfiguration RecordConfiguration } } + /// + /// 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. + /// public bool CheckTrackExists(string trackName) { lock (this) @@ -138,6 +146,10 @@ public bool CheckTrackExists(string trackName) return NativeMethods.k4a_playback_check_track_exists(this.handle, trackName); } } + + /// + /// Get the number of tracks in a playback file. + /// public int TrackCount { get @@ -154,6 +166,13 @@ public int TrackCount } } + /// + /// 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) @@ -185,6 +204,11 @@ public string GetTrackName(int index) } } + /// + /// 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) @@ -203,7 +227,12 @@ public bool GetTrackIsBuiltin(string trackName) } } - RecordVideoSettings GetTrackVideoSettings(string 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) { @@ -225,6 +254,15 @@ RecordVideoSettings GetTrackVideoSettings(string trackName) } } + /// + /// Gets the codec id string for a particular track. + /// + /// + /// The track name to read the codec id from. + /// + /// 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) @@ -256,6 +294,15 @@ public string GetTrackCodecId(string trackName) } } + /// + /// 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) @@ -287,6 +334,11 @@ public byte[] GetTrackCodecContext(string trackName) } } + /// + /// 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) @@ -318,6 +370,15 @@ public string GetTag(string name) } } + /// + /// 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) @@ -331,6 +392,11 @@ public void SetColorConversion(ImageFormat targetFormat) } } + /// + /// Reads an attachment file from a recording. + /// + /// The attachment file name. + /// The attachment data. public byte[] GetAttachment(string fileName) { lock (this) @@ -362,6 +428,24 @@ public byte[] GetAttachment(string fileName) } } + /// + /// 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) @@ -387,6 +471,24 @@ public Capture GetNextCapture() } } + /// + /// 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) @@ -412,6 +514,96 @@ public Capture GetPreviousCapture() } } + /// + /// 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)); + } + + switch (NativeMethods.k4a_playback_get_next_imu_sample(this.handle, out NativeMethods.k4a_imu_sample_t 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)); + } + + switch (NativeMethods.k4a_playback_get_previous_imu_sample(this.handle, out NativeMethods.k4a_imu_sample_t 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) @@ -440,6 +632,22 @@ public DataBlock GetNextDataBlock(string trackName) } } + /// + /// 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) @@ -468,6 +676,28 @@ public DataBlock GetPreviousDataBlock(string trackName) } } + /// + /// 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) @@ -481,6 +711,15 @@ public void Seek(TimeSpan offset, PlaybackSeekOrigin origin = PlaybackSeekOrigin } } + /// + /// Returns 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 @@ -494,21 +733,7 @@ public TimeSpan RecordingLength return TimeSpan.FromTicks(length * 10); } } - - public TimeSpan LastTimestamp - { - get - { - if (this.disposedValue) - { - throw new ObjectDisposedException(nameof(Playback)); - } - - long length = checked((long)NativeMethods.k4a_playback_get_last_timestamp_usec(this.handle)); - return TimeSpan.FromTicks(length * 10); - } - } - + /// public void Dispose() { From ad6f6b67e7c39dd27fd7f48b7d6cd693b8e1dcd4 Mon Sep 17 00:00:00 2001 From: Brent Allen Date: Mon, 7 Oct 2019 09:17:50 -0700 Subject: [PATCH 06/16] Added some exception details --- src/csharp/K4a.sln | 51 +++++ .../AzureKinectCreateRecordingException.cs | 10 +- src/csharp/Record/Record.cs | 4 +- .../Tests/Record.UnitTests/CaptureTests.cs | 184 ++++++++++++++++++ ...zure.Kinect.Sensor.Record.UnitTests.csproj | 61 ++++++ 5 files changed, 304 insertions(+), 6 deletions(-) create mode 100644 src/csharp/Tests/Record.UnitTests/CaptureTests.cs create mode 100644 src/csharp/Tests/Record.UnitTests/Microsoft.Azure.Kinect.Sensor.Record.UnitTests.csproj diff --git a/src/csharp/K4a.sln b/src/csharp/K4a.sln index 9904bdad9..b0094b756 100644 --- a/src/csharp/K4a.sln +++ b/src/csharp/K4a.sln @@ -44,102 +44,152 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Azure.Kinect.Sens 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("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "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|Any CPU + {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Debug|Any CPU.Build.0 = Debug|Any CPU {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Debug|x64.ActiveCfg = Debug|Any CPU {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Debug|x64.Build.0 = Debug|Any CPU {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Debug|x86.ActiveCfg = Debug|Any CPU {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Debug|x86.Build.0 = Debug|Any CPU + {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Release|Any CPU.ActiveCfg = Release|Any CPU + {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Release|Any CPU.Build.0 = Release|Any CPU {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Release|x64.ActiveCfg = Release|Any CPU {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Release|x64.Build.0 = Release|Any CPU {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Release|x86.ActiveCfg = Release|Any CPU {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Release|x86.Build.0 = Release|Any CPU + {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|Any CPU {71EAC57F-4023-4B45-8F9E-4A7C05A6BDB3}.Debug|x64.Build.0 = Debug|Any CPU {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 @@ -155,6 +205,7 @@ Global {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/Exceptions/AzureKinectCreateRecordingException.cs b/src/csharp/Record/Exceptions/AzureKinectCreateRecordingException.cs index 0f3b43457..5f20e5c0d 100644 --- a/src/csharp/Record/Exceptions/AzureKinectCreateRecordingException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectCreateRecordingException.cs @@ -84,9 +84,10 @@ protected AzureKinectCreateRecordingException(string message, ICollection 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(Func function) + internal static void ThrowIfNotSuccess(string fileName, Func function) where T : System.Enum { @@ -95,7 +96,7 @@ internal static void ThrowIfNotSuccess(Func function) T result = function(); if (!AzureKinectRecordException.IsSuccess(result)) { - throw new AzureKinectCreateRecordingException($"result = {result}", tracer.LogMessages); + throw new AzureKinectCreateRecordingException($"fileName = \"{fileName}\"\r\nresult = {result}", tracer.LogMessages); } } } @@ -104,15 +105,16 @@ internal static void ThrowIfNotSuccess(Func function) /// 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(LoggingTracer tracer, T result) + internal static void ThrowIfNotSuccess(string fileName, LoggingTracer tracer, T result) where T : System.Enum { if (!AzureKinectRecordException.IsSuccess(result)) { - throw new AzureKinectCreateRecordingException($"result = {result}", tracer.LogMessages); + throw new AzureKinectCreateRecordingException($"fileName = \"{fileName}\"\r\nresult = {result}", tracer.LogMessages); } } } diff --git a/src/csharp/Record/Record.cs b/src/csharp/Record/Record.cs index 3b82f13d6..8d2585354 100644 --- a/src/csharp/Record/Record.cs +++ b/src/csharp/Record/Record.cs @@ -43,12 +43,12 @@ public static Record Create(string path, Device device, DeviceConfiguration devi // Device.Dispose will take the same lock. lock (device) { - AzureKinectCreateRecordingException.ThrowIfNotSuccess(() => NativeMethods.k4a_record_create(path, device.Handle, NativeMethods.k4a_device_configuration_t.FromDeviceConfiguration(deviceConfiguration), out handle)); + AzureKinectCreateRecordingException.ThrowIfNotSuccess(path, () => NativeMethods.k4a_record_create(path, device.Handle, NativeMethods.k4a_device_configuration_t.FromDeviceConfiguration(deviceConfiguration), out handle)); } } else { - AzureKinectCreateRecordingException.ThrowIfNotSuccess(() => NativeMethods.k4a_record_create(path, IntPtr.Zero, NativeMethods.k4a_device_configuration_t.FromDeviceConfiguration(deviceConfiguration), out handle)); + AzureKinectCreateRecordingException.ThrowIfNotSuccess(path, () => NativeMethods.k4a_record_create(path, IntPtr.Zero, NativeMethods.k4a_device_configuration_t.FromDeviceConfiguration(deviceConfiguration), out handle)); } return new Record(handle); diff --git a/src/csharp/Tests/Record.UnitTests/CaptureTests.cs b/src/csharp/Tests/Record.UnitTests/CaptureTests.cs new file mode 100644 index 000000000..10cc4e855 --- /dev/null +++ b/src/csharp/Tests/Record.UnitTests/CaptureTests.cs @@ -0,0 +1,184 @@ +using System; +using System.Threading; +using Microsoft.Azure.Kinect.Sensor; +using Microsoft.Azure.Kinect.Sensor.Record; +using NUnit.Framework; + +namespace Tests +{ + public class Tests + { + string recordingPath; + [SetUp] + public void Setup() + { + recordingPath = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "testfile.mkv"); + } + + [TearDown] + public void TearDown() + { + System.IO.File.Delete(recordingPath); + } + + + [Test] + public void Test1() + { + DeviceConfiguration deviceConfiguration = new DeviceConfiguration() + { + CameraFPS = FPS.FPS30, + ColorFormat = ImageFormat.ColorBGRA32, + ColorResolution = ColorResolution.R720p, + DepthDelayOffColor = TimeSpan.FromMilliseconds(123), + DepthMode = DepthMode.NFOV_2x2Binned, + DisableStreamingIndicator = true, + SuboridinateDelayOffMaster = TimeSpan.FromMilliseconds(456), + SynchronizedImagesOnly = true, + WiredSyncMode = WiredSyncMode.Subordinate + }; + + using (Record record = Record.Create(this.recordingPath, null, deviceConfiguration)) + { + + record.AddCustomVideoTrack("CustomVideo", "V_CUSTOM1", new byte[] { 1, 2, 3 }, new RecordVideoSettings() { FrameRate = 1, Height = 10, Width = 20 }); + record.AddCustomSubtitleTrack("CustomSubtitle", "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; + + using (Capture c = new Capture()) + { + c.Color = new Image(ImageFormat.ColorBGRA32, 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("CustomVideo", TimeSpan.FromSeconds(timeStamp), customData); + + for (int x = 0; x < customData.Length; x++) + { + customData[x] = (byte)(i + x + 1); + } + record.WriteCustomTrackData("CustomSubtitle", TimeSpan.FromSeconds(timeStamp), customData); + + record.Flush(); + } + } + + + using (Playback playback = Playback.Open(recordingPath)) + { + Assert.IsTrue(playback.CheckTrackExists("CustomVideo")); + Assert.IsTrue(playback.CheckTrackExists("CustomSubtitle")); + Assert.AreEqual("V_CUSTOM1", playback.GetTrackCodecId("CustomVideo")); + Assert.AreEqual(new byte[] { 1, 2, 3 }, playback.GetTrackCodecContext("CustomVideo")); + + for (int i = 0; i < 10; i++) + { + double timeStamp = 10.0 + i * 1.0; + + + using (Capture c = playback.GetNextCapture()) + { + + Assert.AreEqual(25.0f, c.Temperature); + + c.Color = new Image(ImageFormat.ColorBGRA32, 1280, 720); + c.IR = new Image(ImageFormat.IR16, 320, 288); + c.Depth = new Image(ImageFormat.Depth16, 320, 288); + + Assert.AreEqual(ImageFormat.ColorBGRA32, 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); + + Assert.AreEqual(TimeSpan.FromMilliseconds(12), c.Color.Exposure); + Assert.AreEqual(100, c.Color.ISOSpeed); + Assert.AreEqual(0, c.Color.SystemTimestampNsec); + 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, readSample); + } + + byte[] customData = new byte[i + 1]; + for (int x = 0; x < customData.Length; x++) + { + customData[x] = (byte)(i + x); + } + using (DataBlock videoBlock = playback.GetNextDataBlock("CustomVideo")) + { + Assert.AreEqual(customData, videoBlock); + 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("CustomSubtitle")) + { + Assert.AreEqual(customData, subtitleBlock); + Assert.AreEqual(TimeSpan.FromSeconds(timeStamp), subtitleBlock.DeviceTimestamp); + } + } + } + Assert.Pass(); + } + } +} \ 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..77e26d488 --- /dev/null +++ b/src/csharp/Tests/Record.UnitTests/Microsoft.Azure.Kinect.Sensor.Record.UnitTests.csproj @@ -0,0 +1,61 @@ + + + + + netcoreapp2.1 + + false + + x64;x86 + false + ..\..\AzureKinectSensorSDK.ruleset + $(BaseOutputPath)\$(AssemblyName)\ + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + + + stylecop.json + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + From c1696981d4c0e37f4203cfc67b2816596aabb134 Mon Sep 17 00:00:00 2001 From: Brent Allen Date: Tue, 8 Oct 2019 09:57:36 -0700 Subject: [PATCH 07/16] Added tracing redirection to record --- include/k4arecord/record.h | 47 ++++++ .../AzureKinectAddAttachmentException.cs | 2 +- ...reKinectAddCustomSubtitleTrackException.cs | 2 +- ...AzureKinectAddCustomVideoTrackException.cs | 2 +- .../AzureKinectAddImuTrackException.cs | 2 +- .../Exceptions/AzureKinectAddTagException.cs | 5 +- .../AzureKinectCreateRecordingException.cs | 8 +- .../Exceptions/AzureKinectFlushException.cs | 2 +- .../AzureKinectGetCalibrationException.cs | 2 +- .../AzureKinectGetCaptureException.cs | 2 +- .../AzureKinectGetDataBlockException.cs | 2 +- .../AzureKinectGetImuSampleException.cs | 2 +- .../AzureKinectGetRawCalibrationException.cs | 2 +- .../Exceptions/AzureKinectGetTagException.cs | 2 +- ...zureKinectGetTrackCodecContextException.cs | 2 +- .../AzureKinectGetTrackNameException.cs | 2 +- .../AzureKinectOpenPlaybackException.cs | 2 +- .../Exceptions/AzureKinectSeekException.cs | 2 +- .../AzureKinectSetColorConversionException.cs | 2 +- ...ureKinectTrackGetVideoSettingsException.cs | 2 +- .../AzureKinectWriteCaptureException.cs | 2 +- ...zureKinectWriteCustomTrackDataException.cs | 2 +- .../AzureKinectWriteHeaderException.cs | 2 +- .../AzureKinectWriteImuSampleException.cs | 2 +- src/csharp/Record/NativeMethods.cs | 6 +- src/csharp/Record/RecordLogger.cs | 142 ++++++++++++++++++ src/csharp/SDK/LogMessage.cs | 2 +- src/csharp/SDK/Logger.cs | 31 ++++ src/csharp/SDK/Native/LoggingTracer.cs | 31 +++- .../Tests/Record.UnitTests/CaptureTests.cs | 30 ++-- src/record/sdk/record.cpp | 7 + 31 files changed, 300 insertions(+), 51 deletions(-) create mode 100644 src/csharp/Record/RecordLogger.cs diff --git a/include/k4arecord/record.h b/include/k4arecord/record.h index 998ce3d69..137c2409c 100644 --- a/include/k4arecord/record.h +++ b/include/k4arecord/record.h @@ -462,6 +462,53 @@ K4ARECORD_EXPORT k4a_result_t k4a_record_flush(k4a_record_t recording_handle); */ K4ARECORD_EXPORT void k4a_record_close(k4a_record_t recording_handle); + +/** Sets and clears the callback function to receive debug messages from the Azure Kinect record and playback APIs. + * + * \param message_cb + * The callback function to receive messages from. Set to NULL to unregister the callback function. + * + * \param message_cb_context + * The callback functions context. + * + * \param min_level + * The least critical error the user wants to be notified about. + * + * \return ::K4A_RESULT_SUCCEEDED if the callback function was set or cleared successfully. ::K4A_RESULT_FAILED if an + * error is encountered or the callback function has already been set. + * + * \remarks + * Call this function to set or clear the callback function that is used to deliver debug messages to the caller. This + * callback may be called concurrently, it is up to the implementation of the callback function to ensure the + * parallelization is handled. + * + * \remarks + * Clearing the callback function will block until all pending calls to the callback function have completed. + * + * \remarks + * To update \p min_level, k4a_record_set_debug_message_handler() can be called with the same value \p message_cb and by + * specifying a new \p min_level. + * + * \remarks + * Logging provided via this API is independent of the logging controlled by the environmental variable controls \p + * K4A_ENABLE_LOG_TO_STDOUT, K4A_RECORD_ENABLE_LOG_TO_A_FILE, and K4A_LOG_LEVEL. However there is a slight change in + * default behavior when using this function. By default, when k4a_record_set_debug_message_handler() has not been used to + * register a message callback, the default for environmental variable controls is to send debug messages as if + * K4A_ENABLE_LOG_TO_STDOUT=1 were set. If k4a_record_set_debug_message_handler registers a callback function before + * k4a_record_create() or k4a_playback_create() is called, then the default for environmental controls is as if K4A_ENABLE_LOG_TO_STDOUT=0 was + * specified. Physically specifying the environmental control will override the default. + * + * \xmlonly + * + * k4a.h (include k4a/k4a.h) + * k4a.lib + * k4a.dll + * + * \endxmlonly + */ +K4ARECORD_EXPORT k4a_result_t k4a_record_set_debug_message_handler(k4a_logging_message_cb_t *message_cb, + void *message_cb_context, + k4a_log_level_t min_level); /** * @} */ diff --git a/src/csharp/Record/Exceptions/AzureKinectAddAttachmentException.cs b/src/csharp/Record/Exceptions/AzureKinectAddAttachmentException.cs index c18021deb..3dd850417 100644 --- a/src/csharp/Record/Exceptions/AzureKinectAddAttachmentException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectAddAttachmentException.cs @@ -84,7 +84,7 @@ protected AzureKinectAddAttachmentException(string message, ICollection(Func function) where T : System.Enum { - using (LoggingTracer tracer = new LoggingTracer()) + using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider)) { T result = function(); if (!AzureKinectRecordException.IsSuccess(result)) diff --git a/src/csharp/Record/Exceptions/AzureKinectAddCustomSubtitleTrackException.cs b/src/csharp/Record/Exceptions/AzureKinectAddCustomSubtitleTrackException.cs index b967cf532..b75e5fe46 100644 --- a/src/csharp/Record/Exceptions/AzureKinectAddCustomSubtitleTrackException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectAddCustomSubtitleTrackException.cs @@ -84,7 +84,7 @@ protected AzureKinectAddCustomSubtitleTrackException(string message, ICollection internal static void ThrowIfNotSuccess(Func function) where T : System.Enum { - using (LoggingTracer tracer = new LoggingTracer()) + using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider)) { T result = function(); if (!AzureKinectRecordException.IsSuccess(result)) diff --git a/src/csharp/Record/Exceptions/AzureKinectAddCustomVideoTrackException.cs b/src/csharp/Record/Exceptions/AzureKinectAddCustomVideoTrackException.cs index e07ceab5f..efa5be47d 100644 --- a/src/csharp/Record/Exceptions/AzureKinectAddCustomVideoTrackException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectAddCustomVideoTrackException.cs @@ -84,7 +84,7 @@ protected AzureKinectAddCustomVideoTrackException(string message, ICollection(Func function) where T : System.Enum { - using (LoggingTracer tracer = new LoggingTracer()) + using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider)) { T result = function(); if (!AzureKinectRecordException.IsSuccess(result)) diff --git a/src/csharp/Record/Exceptions/AzureKinectAddImuTrackException.cs b/src/csharp/Record/Exceptions/AzureKinectAddImuTrackException.cs index bb0f9b2f8..161c8dde9 100644 --- a/src/csharp/Record/Exceptions/AzureKinectAddImuTrackException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectAddImuTrackException.cs @@ -84,7 +84,7 @@ protected AzureKinectAddImuTrackException(string message, ICollection(Func function) where T : System.Enum { - using (LoggingTracer tracer = new LoggingTracer()) + using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider)) { T result = function(); if (!AzureKinectRecordException.IsSuccess(result)) diff --git a/src/csharp/Record/Exceptions/AzureKinectAddTagException.cs b/src/csharp/Record/Exceptions/AzureKinectAddTagException.cs index 1adaf22fd..43d4f259e 100644 --- a/src/csharp/Record/Exceptions/AzureKinectAddTagException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectAddTagException.cs @@ -7,9 +7,8 @@ using System; using System.Collections.Generic; using System.Runtime.Serialization; -using Microsoft.Azure.Kinect.Sensor.Record.Exceptions; -namespace Microsoft.Azure.Kinect.Sensor +namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions { /// /// Represents errors that occur when adding a tag to a recording @@ -89,7 +88,7 @@ protected AzureKinectAddTagException(string message, ICollection log internal static void ThrowIfNotSuccess(Func function) where T : System.Enum { - using (LoggingTracer tracer = new LoggingTracer()) + using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider)) { T result = function(); if (!AzureKinectRecordException.IsSuccess(result)) diff --git a/src/csharp/Record/Exceptions/AzureKinectCreateRecordingException.cs b/src/csharp/Record/Exceptions/AzureKinectCreateRecordingException.cs index 5f20e5c0d..58506b54c 100644 --- a/src/csharp/Record/Exceptions/AzureKinectCreateRecordingException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectCreateRecordingException.cs @@ -7,9 +7,9 @@ using System; using System.Collections.Generic; using System.Runtime.Serialization; -using Microsoft.Azure.Kinect.Sensor.Record.Exceptions; +using Microsoft.Azure.Kinect.Sensor.Record; -namespace Microsoft.Azure.Kinect.Sensor +namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions { /// /// Represents errors that occur when creating an Azure Kinect sensor recording. @@ -90,8 +90,8 @@ protected AzureKinectCreateRecordingException(string message, ICollection(string fileName, Func function) where T : System.Enum { - - using (LoggingTracer tracer = new LoggingTracer()) + + using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider)) { T result = function(); if (!AzureKinectRecordException.IsSuccess(result)) diff --git a/src/csharp/Record/Exceptions/AzureKinectFlushException.cs b/src/csharp/Record/Exceptions/AzureKinectFlushException.cs index 6b24c2c8a..c0021f44d 100644 --- a/src/csharp/Record/Exceptions/AzureKinectFlushException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectFlushException.cs @@ -84,7 +84,7 @@ protected AzureKinectFlushException(string message, ICollection logM internal static void ThrowIfNotSuccess(Func function) where T : System.Enum { - using (LoggingTracer tracer = new LoggingTracer()) + using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider)) { T result = function(); if (!AzureKinectRecordException.IsSuccess(result)) diff --git a/src/csharp/Record/Exceptions/AzureKinectGetCalibrationException.cs b/src/csharp/Record/Exceptions/AzureKinectGetCalibrationException.cs index dbeb25d72..48b45ab64 100644 --- a/src/csharp/Record/Exceptions/AzureKinectGetCalibrationException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectGetCalibrationException.cs @@ -84,7 +84,7 @@ protected AzureKinectGetCalibrationException(string message, ICollection(Func function) where T : System.Enum { - using (LoggingTracer tracer = new LoggingTracer()) + using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider)) { T result = function(); if (!AzureKinectRecordException.IsSuccess(result)) diff --git a/src/csharp/Record/Exceptions/AzureKinectGetCaptureException.cs b/src/csharp/Record/Exceptions/AzureKinectGetCaptureException.cs index f1d08d2c5..7e4ec0b71 100644 --- a/src/csharp/Record/Exceptions/AzureKinectGetCaptureException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectGetCaptureException.cs @@ -84,7 +84,7 @@ protected AzureKinectGetCaptureException(string message, ICollection internal static void ThrowIfNotSuccess(Func function) where T : System.Enum { - using (LoggingTracer tracer = new LoggingTracer()) + using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider)) { T result = function(); if (!AzureKinectRecordException.IsSuccess(result)) diff --git a/src/csharp/Record/Exceptions/AzureKinectGetDataBlockException.cs b/src/csharp/Record/Exceptions/AzureKinectGetDataBlockException.cs index 4180488fc..f3685e9c8 100644 --- a/src/csharp/Record/Exceptions/AzureKinectGetDataBlockException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectGetDataBlockException.cs @@ -84,7 +84,7 @@ protected AzureKinectGetDataBlockException(string message, ICollection(Func function) where T : System.Enum { - using (LoggingTracer tracer = new LoggingTracer()) + using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider)) { T result = function(); if (!AzureKinectRecordException.IsSuccess(result)) diff --git a/src/csharp/Record/Exceptions/AzureKinectGetImuSampleException.cs b/src/csharp/Record/Exceptions/AzureKinectGetImuSampleException.cs index d58523c3d..d06addf96 100644 --- a/src/csharp/Record/Exceptions/AzureKinectGetImuSampleException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectGetImuSampleException.cs @@ -84,7 +84,7 @@ protected AzureKinectGetImuSampleException(string message, ICollection(Func function) where T : System.Enum { - using (LoggingTracer tracer = new LoggingTracer()) + using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider)) { T result = function(); if (!AzureKinectRecordException.IsSuccess(result)) diff --git a/src/csharp/Record/Exceptions/AzureKinectGetRawCalibrationException.cs b/src/csharp/Record/Exceptions/AzureKinectGetRawCalibrationException.cs index 7798495c7..ec7d0aca8 100644 --- a/src/csharp/Record/Exceptions/AzureKinectGetRawCalibrationException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectGetRawCalibrationException.cs @@ -84,7 +84,7 @@ protected AzureKinectGetRawCalibrationException(string message, ICollection(Func function) where T : System.Enum { - using (LoggingTracer tracer = new LoggingTracer()) + using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider)) { T result = function(); if (!AzureKinectRecordException.IsSuccess(result)) diff --git a/src/csharp/Record/Exceptions/AzureKinectGetTagException.cs b/src/csharp/Record/Exceptions/AzureKinectGetTagException.cs index dd1627cc2..afcaae86b 100644 --- a/src/csharp/Record/Exceptions/AzureKinectGetTagException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectGetTagException.cs @@ -84,7 +84,7 @@ protected AzureKinectGetTagException(string message, ICollection log internal static void ThrowIfNotSuccess(Func function) where T : System.Enum { - using (LoggingTracer tracer = new LoggingTracer()) + using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider)) { T result = function(); if (!AzureKinectRecordException.IsSuccess(result)) diff --git a/src/csharp/Record/Exceptions/AzureKinectGetTrackCodecContextException.cs b/src/csharp/Record/Exceptions/AzureKinectGetTrackCodecContextException.cs index 3a2f47ab1..2181246c3 100644 --- a/src/csharp/Record/Exceptions/AzureKinectGetTrackCodecContextException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectGetTrackCodecContextException.cs @@ -84,7 +84,7 @@ protected AzureKinectGetTrackCodecContextException(string message, ICollection(Func function) where T : System.Enum { - using (LoggingTracer tracer = new LoggingTracer()) + using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider)) { T result = function(); if (!AzureKinectRecordException.IsSuccess(result)) diff --git a/src/csharp/Record/Exceptions/AzureKinectGetTrackNameException.cs b/src/csharp/Record/Exceptions/AzureKinectGetTrackNameException.cs index 18543fdda..977192c51 100644 --- a/src/csharp/Record/Exceptions/AzureKinectGetTrackNameException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectGetTrackNameException.cs @@ -84,7 +84,7 @@ protected AzureKinectGetTrackNameException(string message, ICollection(Func function) where T : System.Enum { - using (LoggingTracer tracer = new LoggingTracer()) + using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider)) { T result = function(); if (!AzureKinectRecordException.IsSuccess(result)) diff --git a/src/csharp/Record/Exceptions/AzureKinectOpenPlaybackException.cs b/src/csharp/Record/Exceptions/AzureKinectOpenPlaybackException.cs index 4ab1a3b10..1725c53de 100644 --- a/src/csharp/Record/Exceptions/AzureKinectOpenPlaybackException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectOpenPlaybackException.cs @@ -84,7 +84,7 @@ protected AzureKinectOpenPlaybackException(string message, ICollection(Func function) where T : System.Enum { - using (LoggingTracer tracer = new LoggingTracer()) + using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider)) { T result = function(); if (!AzureKinectRecordException.IsSuccess(result)) diff --git a/src/csharp/Record/Exceptions/AzureKinectSeekException.cs b/src/csharp/Record/Exceptions/AzureKinectSeekException.cs index 20f0213c4..70700af7d 100644 --- a/src/csharp/Record/Exceptions/AzureKinectSeekException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectSeekException.cs @@ -84,7 +84,7 @@ protected AzureKinectSeekException(string message, ICollection logMe internal static void ThrowIfNotSuccess(Func function) where T : System.Enum { - using (LoggingTracer tracer = new LoggingTracer()) + using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider)) { T result = function(); if (!AzureKinectRecordException.IsSuccess(result)) diff --git a/src/csharp/Record/Exceptions/AzureKinectSetColorConversionException.cs b/src/csharp/Record/Exceptions/AzureKinectSetColorConversionException.cs index a2c9d3f32..6de3cb76e 100644 --- a/src/csharp/Record/Exceptions/AzureKinectSetColorConversionException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectSetColorConversionException.cs @@ -84,7 +84,7 @@ protected AzureKinectSetColorConversionException(string message, ICollection(Func function) where T : System.Enum { - using (LoggingTracer tracer = new LoggingTracer()) + using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider)) { T result = function(); if (!AzureKinectRecordException.IsSuccess(result)) diff --git a/src/csharp/Record/Exceptions/AzureKinectTrackGetVideoSettingsException.cs b/src/csharp/Record/Exceptions/AzureKinectTrackGetVideoSettingsException.cs index a5fc9363a..98b157a9e 100644 --- a/src/csharp/Record/Exceptions/AzureKinectTrackGetVideoSettingsException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectTrackGetVideoSettingsException.cs @@ -84,7 +84,7 @@ protected AzureKinectTrackGetVideoSettingsException(string message, ICollection< internal static void ThrowIfNotSuccess(Func function) where T : System.Enum { - using (LoggingTracer tracer = new LoggingTracer()) + using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider)) { T result = function(); if (!AzureKinectRecordException.IsSuccess(result)) diff --git a/src/csharp/Record/Exceptions/AzureKinectWriteCaptureException.cs b/src/csharp/Record/Exceptions/AzureKinectWriteCaptureException.cs index 15d5c3f7c..162da069a 100644 --- a/src/csharp/Record/Exceptions/AzureKinectWriteCaptureException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectWriteCaptureException.cs @@ -84,7 +84,7 @@ protected AzureKinectWriteCaptureException(string message, ICollection(Func function) where T : System.Enum { - using (LoggingTracer tracer = new LoggingTracer()) + using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider)) { T result = function(); if (!AzureKinectRecordException.IsSuccess(result)) diff --git a/src/csharp/Record/Exceptions/AzureKinectWriteCustomTrackDataException.cs b/src/csharp/Record/Exceptions/AzureKinectWriteCustomTrackDataException.cs index 2358083b1..4244a4c66 100644 --- a/src/csharp/Record/Exceptions/AzureKinectWriteCustomTrackDataException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectWriteCustomTrackDataException.cs @@ -84,7 +84,7 @@ protected AzureKinectWriteCustomTrackDataException(string message, ICollection(Func function) where T : System.Enum { - using (LoggingTracer tracer = new LoggingTracer()) + using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider)) { T result = function(); if (!AzureKinectRecordException.IsSuccess(result)) diff --git a/src/csharp/Record/Exceptions/AzureKinectWriteHeaderException.cs b/src/csharp/Record/Exceptions/AzureKinectWriteHeaderException.cs index 23e97e83b..fcaba6d9b 100644 --- a/src/csharp/Record/Exceptions/AzureKinectWriteHeaderException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectWriteHeaderException.cs @@ -84,7 +84,7 @@ protected AzureKinectWriteHeaderException(string message, ICollection(Func function) where T : System.Enum { - using (LoggingTracer tracer = new LoggingTracer()) + using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider)) { T result = function(); if (!AzureKinectRecordException.IsSuccess(result)) diff --git a/src/csharp/Record/Exceptions/AzureKinectWriteImuSampleException.cs b/src/csharp/Record/Exceptions/AzureKinectWriteImuSampleException.cs index 9b031f2fb..0278cae03 100644 --- a/src/csharp/Record/Exceptions/AzureKinectWriteImuSampleException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectWriteImuSampleException.cs @@ -84,7 +84,7 @@ protected AzureKinectWriteImuSampleException(string message, ICollection(Func function) where T : System.Enum { - using (LoggingTracer tracer = new LoggingTracer()) + using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider)) { T result = function(); if (!AzureKinectRecordException.IsSuccess(result)) diff --git a/src/csharp/Record/NativeMethods.cs b/src/csharp/Record/NativeMethods.cs index 36de967d0..7d3ed31c9 100644 --- a/src/csharp/Record/NativeMethods.cs +++ b/src/csharp/Record/NativeMethods.cs @@ -49,9 +49,9 @@ public enum k4a_stream_result_t K4A_STREAM_RESULT_FAILED, K4A_STREAM_RESULT_EOF, } - - [DllImport("k4a", CallingConvention = k4aCallingConvention)] - public static extern k4a_result_t k4a_set_debug_message_handler( + + [DllImport("k4arecord", CallingConvention = k4aCallingConvention)] + public static extern k4a_result_t k4a_record_set_debug_message_handler( k4a_logging_message_cb_t message_cb, IntPtr message_cb_context, LogLevel min_level); diff --git a/src/csharp/Record/RecordLogger.cs b/src/csharp/Record/RecordLogger.cs new file mode 100644 index 000000000..ea644c6f8 --- /dev/null +++ b/src/csharp/Record/RecordLogger.cs @@ -0,0 +1,142 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; + +namespace Microsoft.Azure.Kinect.Sensor.Record +{ + /// + /// The Azure Kinect logging system. Enables access to the debug messages from the the Azure Kinect Record and Playback API. + /// + public static class RecordLogger + { + private static readonly object SyncRoot = new object(); + private static readonly NativeMethods.k4a_logging_message_cb_t DebugMessageHandler = OnDebugMessage; + private static bool isInitialized; + + private static event Action LogMessageHandlers; + + private class RecordLoggerProvider : ILoggingProvider + { + public event Action LogMessage + { + add + { + RecordLogger.LogMessage += value; + } + remove + { + RecordLogger.LogMessage -= value; + } + } + } + + private readonly static RecordLoggerProvider loggerProvider = new RecordLoggerProvider(); + + public static ILoggingProvider LogProvider + { + get + { + return RecordLogger.loggerProvider; + } + } + + /// + /// Occurs when the Azure Kinect Sensor Record and Playback SDK delivers a debug message. + /// + public static event Action LogMessage + { + add + { + lock (SyncRoot) + { + if (!RecordLogger.isInitialized) + { + RecordLogger.Initialize(); + } + + LogMessageHandlers += value; + } + } + + remove + { + lock (SyncRoot) + { + LogMessageHandlers -= value; + } + } + } + + /// + /// Initializes the class to begin receiving messages from the Azure Kinect Sensor SDK. + /// + public static void Initialize() + { + lock (SyncRoot) + { + if (RecordLogger.isInitialized) + { + return; + } + + AppDomain.CurrentDomain.ProcessExit += CurrentDomain_Exit; + AppDomain.CurrentDomain.DomainUnload += CurrentDomain_Exit; + NativeMethods.k4a_result_t result = NativeMethods.k4a_record_set_debug_message_handler(DebugMessageHandler, IntPtr.Zero, LogLevel.Trace); + if (result != NativeMethods.k4a_result_t.K4A_RESULT_SUCCEEDED) + { + throw new AzureKinectException("Failed to set the Debug Message Handler"); + } + + RecordLogger.isInitialized = true; + } + } + + /// + /// Resets the logger to an uninitialized state. This is used in the Unit Tests to ensure that the + /// initialization is run during the unit tests. + /// + internal static void Reset() + { + lock (SyncRoot) + { + if (!RecordLogger.isInitialized) + { + return; + } + + AppDomain.CurrentDomain.ProcessExit -= CurrentDomain_Exit; + AppDomain.CurrentDomain.DomainUnload -= CurrentDomain_Exit; + + // TODO: This won't work as Raise Error has an invalid pointer. + ////NativeMethods.k4a_result_t result = NativeMethods.k4a_set_debug_message_handler(null, IntPtr.Zero, LogLevel.Trace); + ////if (result != NativeMethods.k4a_result_t.K4A_RESULT_SUCCEEDED) + ////{ + //// throw new AzureKinectException("Failed to set the Debug Message Handler"); + ////} + + RecordLogger.isInitialized = false; + } + } + + private static void OnDebugMessage(IntPtr context, LogLevel level, string file, int line, string message) + { + LogMessage data = new LogMessage(DateTime.Now, level, file, line, message); + + Action eventhandler = LogMessageHandlers; + if (eventhandler != null) + { + eventhandler(data); + } + } + + private static void CurrentDomain_Exit(object sender, EventArgs e) + { + NativeMethods.k4a_result_t result = NativeMethods.k4a_record_set_debug_message_handler(null, IntPtr.Zero, LogLevel.Off); + if (result != NativeMethods.k4a_result_t.K4A_RESULT_SUCCEEDED) + { + Trace.WriteLine("Failed to close the debug message handler"); + } + } + } +} diff --git a/src/csharp/SDK/LogMessage.cs b/src/csharp/SDK/LogMessage.cs index 820b2a8e2..f60f37619 100644 --- a/src/csharp/SDK/LogMessage.cs +++ b/src/csharp/SDK/LogMessage.cs @@ -21,7 +21,7 @@ public class LogMessage /// The file name of the source file that generated the message. /// The line number of the source file that generated the message. /// The messaged generated by the Azure Kinect SDK. - internal LogMessage(DateTime time, LogLevel logLevel, string fileName, int line, string message) + public LogMessage(DateTime time, LogLevel logLevel, string fileName, int line, string message) { this.Time = time; this.LogLevel = logLevel; diff --git a/src/csharp/SDK/Logger.cs b/src/csharp/SDK/Logger.cs index 50dfecad8..c289eeae6 100644 --- a/src/csharp/SDK/Logger.cs +++ b/src/csharp/SDK/Logger.cs @@ -9,6 +9,12 @@ namespace Microsoft.Azure.Kinect.Sensor { + + public interface ILoggingProvider + { + event Action LogMessage; + } + /// /// The Azure Kinect logging system. Enables access to the debug messages from the Azure Kinect device. /// @@ -20,6 +26,31 @@ public static class Logger private static event Action LogMessageHandlers; + private class LoggerProvider : ILoggingProvider + { + public event Action LogMessage + { + add + { + Logger.LogMessage += value; + } + remove + { + Logger.LogMessage -= value; + } + } + } + + private readonly static LoggerProvider loggerProvider = new LoggerProvider(); + + public static ILoggingProvider LogProvider + { + get + { + return Logger.loggerProvider; + } + } + /// /// Occurs when the Azure Kinect Sensor SDK delivers a debug message. /// diff --git a/src/csharp/SDK/Native/LoggingTracer.cs b/src/csharp/SDK/Native/LoggingTracer.cs index 5c7a230b4..71291ad7c 100644 --- a/src/csharp/SDK/Native/LoggingTracer.cs +++ b/src/csharp/SDK/Native/LoggingTracer.cs @@ -21,14 +21,30 @@ public class LoggingTracer : IDisposable private bool disposed; private List messages; + private ILoggingProvider[] loggingProviders; + + private LogLevel minLevel; + /// /// Initializes a new instance of the class. /// public LoggingTracer() + : this(LogLevel.Warning, Logger.LogProvider) + { + } + + public LoggingTracer(LogLevel minLevel, params ILoggingProvider[] loggingProvider) { this.messages = new List(); this.threadId = Thread.CurrentThread.ManagedThreadId; - Logger.LogMessage += this.Logger_LogMessage; + this.minLevel = minLevel; + + this.loggingProviders = (ILoggingProvider[])loggingProvider.Clone(); + + foreach (ILoggingProvider provider in this.loggingProviders) + { + provider.LogMessage += this.Logger_LogMessage; + } } /// @@ -71,7 +87,12 @@ protected virtual void Dispose(bool disposing) { if (disposing) { - Logger.LogMessage -= this.Logger_LogMessage; + foreach (ILoggingProvider provider in this.loggingProviders) + { + provider.LogMessage -= this.Logger_LogMessage; + } + + this.loggingProviders = null; // There are no longer any tracers on this thread. Clear the message list to allow the memory to be freed. this.messages = null; @@ -89,6 +110,12 @@ private void Logger_LogMessage(LogMessage logMessage) return; } + if (logMessage.LogLevel > this.minLevel) + { + // The log messages are too verbose. Ignore them. + return; + } + this.messages.Add(logMessage); } } diff --git a/src/csharp/Tests/Record.UnitTests/CaptureTests.cs b/src/csharp/Tests/Record.UnitTests/CaptureTests.cs index 10cc4e855..a2e4717da 100644 --- a/src/csharp/Tests/Record.UnitTests/CaptureTests.cs +++ b/src/csharp/Tests/Record.UnitTests/CaptureTests.cs @@ -28,7 +28,7 @@ public void Test1() DeviceConfiguration deviceConfiguration = new DeviceConfiguration() { CameraFPS = FPS.FPS30, - ColorFormat = ImageFormat.ColorBGRA32, + ColorFormat = ImageFormat.ColorNV12, ColorResolution = ColorResolution.R720p, DepthDelayOffColor = TimeSpan.FromMilliseconds(123), DepthMode = DepthMode.NFOV_2x2Binned, @@ -41,8 +41,8 @@ public void Test1() using (Record record = Record.Create(this.recordingPath, null, deviceConfiguration)) { - record.AddCustomVideoTrack("CustomVideo", "V_CUSTOM1", new byte[] { 1, 2, 3 }, new RecordVideoSettings() { FrameRate = 1, Height = 10, Width = 20 }); - record.AddCustomSubtitleTrack("CustomSubtitle", "S_CUSTOM1", new byte[] { 4, 5, 6, 7 }, new RecordSubtitleSettings() { HighFrequencyData = false}); + 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"); @@ -54,7 +54,7 @@ public void Test1() using (Capture c = new Capture()) { - c.Color = new Image(ImageFormat.ColorBGRA32, 1280, 720); + 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; @@ -93,13 +93,13 @@ public void Test1() { customData[x] = (byte)(i + x); } - record.WriteCustomTrackData("CustomVideo", TimeSpan.FromSeconds(timeStamp), customData); + record.WriteCustomTrackData("CUSTOM_VIDEO", TimeSpan.FromSeconds(timeStamp), customData); for (int x = 0; x < customData.Length; x++) { customData[x] = (byte)(i + x + 1); } - record.WriteCustomTrackData("CustomSubtitle", TimeSpan.FromSeconds(timeStamp), customData); + record.WriteCustomTrackData("CUSTOM_SUBTITLE", TimeSpan.FromSeconds(timeStamp), customData); record.Flush(); } @@ -108,10 +108,10 @@ public void Test1() using (Playback playback = Playback.Open(recordingPath)) { - Assert.IsTrue(playback.CheckTrackExists("CustomVideo")); - Assert.IsTrue(playback.CheckTrackExists("CustomSubtitle")); - Assert.AreEqual("V_CUSTOM1", playback.GetTrackCodecId("CustomVideo")); - Assert.AreEqual(new byte[] { 1, 2, 3 }, playback.GetTrackCodecContext("CustomVideo")); + 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")); for (int i = 0; i < 10; i++) { @@ -123,11 +123,7 @@ public void Test1() Assert.AreEqual(25.0f, c.Temperature); - c.Color = new Image(ImageFormat.ColorBGRA32, 1280, 720); - c.IR = new Image(ImageFormat.IR16, 320, 288); - c.Depth = new Image(ImageFormat.Depth16, 320, 288); - - Assert.AreEqual(ImageFormat.ColorBGRA32, c.Color.Format); + Assert.AreEqual(ImageFormat.ColorNV12, c.Color.Format); Assert.AreEqual(1280, c.Color.WidthPixels); Assert.AreEqual(720, c.Color.HeightPixels); @@ -161,7 +157,7 @@ public void Test1() { customData[x] = (byte)(i + x); } - using (DataBlock videoBlock = playback.GetNextDataBlock("CustomVideo")) + using (DataBlock videoBlock = playback.GetNextDataBlock("CUSTOM_VIDEO")) { Assert.AreEqual(customData, videoBlock); Assert.AreEqual(TimeSpan.FromSeconds(timeStamp), videoBlock.DeviceTimestamp); @@ -171,7 +167,7 @@ public void Test1() { customData[x] = (byte)(i + x + 1); } - using (DataBlock subtitleBlock = playback.GetNextDataBlock("CustomSubtitle")) + using (DataBlock subtitleBlock = playback.GetNextDataBlock("CUSTOM_SUBTITLE")) { Assert.AreEqual(customData, subtitleBlock); Assert.AreEqual(TimeSpan.FromSeconds(timeStamp), subtitleBlock.DeviceTimestamp); diff --git a/src/record/sdk/record.cpp b/src/record/sdk/record.cpp index 97d8ede60..aed9f109b 100644 --- a/src/record/sdk/record.cpp +++ b/src/record/sdk/record.cpp @@ -14,6 +14,13 @@ using namespace k4arecord; using namespace LIBMATROSKA_NAMESPACE; +k4a_result_t k4a_record_set_debug_message_handler(k4a_logging_message_cb_t *message_cb, + void *message_cb_context, + k4a_log_level_t min_level) +{ + return logger_register_message_callback(message_cb, message_cb_context, min_level); +} + k4a_result_t k4a_record_create(const char *path, k4a_device_t device, const k4a_device_configuration_t device_config, From f876d18dd4466dc0f3e3aa3b453b82af8d1b3563 Mon Sep 17 00:00:00 2001 From: Brent Allen Date: Tue, 8 Oct 2019 10:26:05 -0700 Subject: [PATCH 08/16] Enabled implicit stride in k4a_image_create --- include/k4a/k4a.h | 1 + src/csharp/SDK/Image.cs | 46 +--------------------- src/image/image.c | 30 ++++++++++++++ tests/UnitTests/allocator_ut/allocator.cpp | 29 ++++++++++++-- 4 files changed, 58 insertions(+), 48 deletions(-) diff --git a/include/k4a/k4a.h b/include/k4a/k4a.h index 2cf9e25bc..5f673dad2 100644 --- a/include/k4a/k4a.h +++ b/include/k4a/k4a.h @@ -595,6 +595,7 @@ K4A_EXPORT float k4a_capture_get_temperature_c(k4a_capture_t capture_handle); * * \param stride_bytes * The number of bytes per horizontal line of the image. + * If set to 0, the stride will be set to the minimum size given the \p format and \p width_pixels. * * \param image_handle * Pointer to store image handle in. diff --git a/src/csharp/SDK/Image.cs b/src/csharp/SDK/Image.cs index a2a41785e..5112252e6 100644 --- a/src/csharp/SDK/Image.cs +++ b/src/csharp/SDK/Image.cs @@ -46,8 +46,8 @@ public class Image : IMemoryOwner, IDisposable /// The pixel format of the image. Must be a format with a constant pixel size. /// Width of the image in pixels. /// Height of the image in pixels. - /// Stride of the image in bytes. Must be as large as the width times the size of a pixel. - public Image(ImageFormat format, int widthPixels, int heightPixels, int strideBytes) + /// Stride of the image in bytes. Must be as large as the width times the size of a pixel. Set to zero for the default if available for that format. + public Image(ImageFormat format, int widthPixels, int heightPixels, int strideBytes = 0) { // Hook the native allocator and register this object. // .Dispose() will be called on this object when the allocator is shut down. @@ -61,48 +61,6 @@ public Image(ImageFormat format, int widthPixels, int heightPixels, int strideBy out this.handle)); } - /// - /// Initializes a new instance of the class. - /// - /// The pixel format of the image. Must be a format with a constant pixel size. - /// Width of the image in pixels. - /// Height of the image in pixels. - public Image(ImageFormat format, int widthPixels, int heightPixels) - { - int pixelSize; - switch (format) - { - case ImageFormat.ColorBGRA32: - pixelSize = 4; - break; - case ImageFormat.Depth16: - case ImageFormat.IR16: - case ImageFormat.Custom16: - pixelSize = 2; - break; - case ImageFormat.Custom8: - pixelSize = 1; - break; - default: - throw new AzureKinectException($"Unable to allocate array for format {format}"); - } - - int stride_bytes = widthPixels * pixelSize; - - // Hook the native allocator and register this object. - // .Dispose() will be called on this object when the allocator is shut down. - Allocator.Singleton.RegisterForDisposal(this); - -#pragma warning disable CA2000 // Dispose objects before losing scope - AzureKinectException.ThrowIfNotSuccess(() => NativeMethods.k4a_image_create( - format, - widthPixels, - heightPixels, - stride_bytes, - image_handle: out this.handle)); -#pragma warning restore CA2000 // Dispose objects before losing scope - } - /// /// Initializes a new instance of the class. /// diff --git a/src/image/image.c b/src/image/image.c index 498961503..ba47800ed 100644 --- a/src/image/image.c +++ b/src/image/image.c @@ -186,6 +186,12 @@ k4a_result_t image_create(k4a_image_format_t format, case K4A_IMAGE_FORMAT_COLOR_NV12: { + if (stride_bytes == 0) + { + // If stride isn't specified, assume the minimum stride + stride_bytes = width_pixels; + } + if (height_pixels % 2 != 0) { LOG_ERROR("NV12 requires an even number of lines. Height %d is invalid.", height_pixels); @@ -215,6 +221,12 @@ k4a_result_t image_create(k4a_image_format_t format, // 1 Byte per pixel case K4A_IMAGE_FORMAT_CUSTOM8: { + if (stride_bytes == 0) + { + // If stride isn't specified, assume the minimum stride + stride_bytes = width_pixels; + } + if (stride_bytes < 1 * width_pixels) { LOG_ERROR("Insufficient stride (%d bytes) to represent image width (%d pixels).", @@ -235,6 +247,12 @@ k4a_result_t image_create(k4a_image_format_t format, case K4A_IMAGE_FORMAT_IR16: case K4A_IMAGE_FORMAT_CUSTOM16: { + if (stride_bytes == 0) + { + // If stride isn't specified, assume the minimum stride + stride_bytes = width_pixels * 2; + } + if (stride_bytes < 2 * width_pixels) { LOG_ERROR("Insufficient stride (%d bytes) to represent image width (%d pixels).", @@ -253,6 +271,12 @@ k4a_result_t image_create(k4a_image_format_t format, // 2 Bytes per pixel case K4A_IMAGE_FORMAT_COLOR_YUY2: { + if (stride_bytes == 0) + { + // If stride isn't specified, assume the minimum stride + stride_bytes = width_pixels * 2; + } + if (width_pixels % 2 != 0) { LOG_ERROR("YUY2 requires an even number of pixels per line. Width of %d is invalid.", width_pixels); @@ -276,6 +300,12 @@ k4a_result_t image_create(k4a_image_format_t format, // 4 Bytes per pixel case K4A_IMAGE_FORMAT_COLOR_BGRA32: { + if (stride_bytes == 0) + { + // If stride isn't specified, assume the minimum stride + stride_bytes = width_pixels * 4; + } + if (stride_bytes < 4 * width_pixels) { LOG_ERROR("Insufficient stride (%d bytes) to represent image width (%d pixels).", diff --git a/tests/UnitTests/allocator_ut/allocator.cpp b/tests/UnitTests/allocator_ut/allocator.cpp index 00291ce33..aa819b583 100644 --- a/tests/UnitTests/allocator_ut/allocator.cpp +++ b/tests/UnitTests/allocator_ut/allocator.cpp @@ -190,10 +190,6 @@ TEST(allocator_ut, image_api_validation) ASSERT_EQ(K4A_RESULT_FAILED, image_create(K4A_IMAGE_FORMAT_COLOR_NV12, 10, 10, 1, ALLOCATION_SOURCE_USER, NULL)); - // Stride of zero - ASSERT_EQ(K4A_RESULT_FAILED, - image_create(K4A_IMAGE_FORMAT_COLOR_NV12, 10, 10, 0, ALLOCATION_SOURCE_USER, &image)); - // Stride length // Validate a valid length and an invalid length @@ -217,6 +213,11 @@ TEST(allocator_ut, image_api_validation) // Odd number of columns ASSERT_EQ(K4A_RESULT_FAILED, image_create(K4A_IMAGE_FORMAT_COLOR_NV12, 11, 10, 20, ALLOCATION_SOURCE_USER, &image)); + // Stride of zero (should succeed and infer the minimum stride) + ASSERT_EQ(K4A_RESULT_SUCCEEDED, + image_create(K4A_IMAGE_FORMAT_COLOR_NV12, 10, 10, 0, ALLOCATION_SOURCE_USER, &image)); + ASSERT_EQ(10, image_get_stride_bytes(image)); + image_dec_ref(image); // YUY2 // Minimum stride @@ -240,6 +241,11 @@ TEST(allocator_ut, image_api_validation) // Odd number of columns ASSERT_EQ(K4A_RESULT_FAILED, image_create(K4A_IMAGE_FORMAT_COLOR_YUY2, 11, 10, 20, ALLOCATION_SOURCE_USER, &image)); + // Stride of zero (should succeed and infer the minimum stride) + ASSERT_EQ(K4A_RESULT_SUCCEEDED, + image_create(K4A_IMAGE_FORMAT_COLOR_YUY2, 10, 10, 0, ALLOCATION_SOURCE_USER, &image)); + ASSERT_EQ(10 * 2, image_get_stride_bytes(image)); + image_dec_ref(image); // BGRA32 // Minimum stride @@ -250,6 +256,11 @@ TEST(allocator_ut, image_api_validation) // Insufficient stride ASSERT_EQ(K4A_RESULT_FAILED, image_create(K4A_IMAGE_FORMAT_COLOR_BGRA32, 10, 10, 39, ALLOCATION_SOURCE_USER, &image)); + // Stride of zero (should succeed and infer the minimum stride) + ASSERT_EQ(K4A_RESULT_SUCCEEDED, + image_create(K4A_IMAGE_FORMAT_COLOR_BGRA32, 10, 10, 0, ALLOCATION_SOURCE_USER, &image)); + ASSERT_EQ(10 * 4, image_get_stride_bytes(image)); + image_dec_ref(image); // MJPEG (no length is valid) ASSERT_EQ(K4A_RESULT_FAILED, @@ -264,6 +275,11 @@ TEST(allocator_ut, image_api_validation) // Insufficient stride ASSERT_EQ(K4A_RESULT_FAILED, image_create(K4A_IMAGE_FORMAT_DEPTH16, 10, 10, 19, ALLOCATION_SOURCE_USER, &image)); + // Stride of zero (should succeed and infer the minimum stride) + ASSERT_EQ(K4A_RESULT_SUCCEEDED, + image_create(K4A_IMAGE_FORMAT_DEPTH16, 10, 10, 0, ALLOCATION_SOURCE_USER, &image)); + ASSERT_EQ(10 * 2, image_get_stride_bytes(image)); + image_dec_ref(image); // Custom8 // Minimum stride @@ -274,6 +290,11 @@ TEST(allocator_ut, image_api_validation) // Insufficient stride ASSERT_EQ(K4A_RESULT_FAILED, (int)image_create(K4A_IMAGE_FORMAT_CUSTOM8, 10, 10, 9, ALLOCATION_SOURCE_USER, &image)); + // Stride of zero (should succeed and infer the minimum stride) + ASSERT_EQ(K4A_RESULT_SUCCEEDED, + image_create(K4A_IMAGE_FORMAT_CUSTOM8, 10, 10, 0, ALLOCATION_SOURCE_USER, &image)); + ASSERT_EQ(10 * 1, image_get_stride_bytes(image)); + image_dec_ref(image); // Height of zero ASSERT_EQ(K4A_RESULT_FAILED, From 17977589f862bd40e0c89ef1d1ec5524f645d2ef Mon Sep 17 00:00:00 2001 From: Brent Allen Date: Tue, 8 Oct 2019 10:51:12 -0700 Subject: [PATCH 09/16] Removed default paramter in public API --- src/csharp/SDK/Image.cs | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/csharp/SDK/Image.cs b/src/csharp/SDK/Image.cs index 5112252e6..8ba3e6217 100644 --- a/src/csharp/SDK/Image.cs +++ b/src/csharp/SDK/Image.cs @@ -47,7 +47,7 @@ public class Image : IMemoryOwner, IDisposable /// Width of the image in pixels. /// Height of the image in pixels. /// Stride of the image in bytes. Must be as large as the width times the size of a pixel. Set to zero for the default if available for that format. - public Image(ImageFormat format, int widthPixels, int heightPixels, int strideBytes = 0) + public Image(ImageFormat format, int widthPixels, int heightPixels, int strideBytes) { // Hook the native allocator and register this object. // .Dispose() will be called on this object when the allocator is shut down. @@ -61,6 +61,28 @@ public Image(ImageFormat format, int widthPixels, int heightPixels, int strideBy out this.handle)); } + /// + /// Initializes a new instance of the class. + /// + /// The pixel format of the image. Must be a format with a constant pixel size. + /// Width of the image in pixels. + /// Height of the image in pixels. + public Image(ImageFormat format, int widthPixels, int heightPixels) + { + // Hook the native allocator and register this object. + // .Dispose() will be called on this object when the allocator is shut down. + Allocator.Singleton.RegisterForDisposal(this); + +#pragma warning disable CA2000 // Dispose objects before losing scope + AzureKinectException.ThrowIfNotSuccess(() => NativeMethods.k4a_image_create( + format, + widthPixels, + heightPixels, + 0, + image_handle: out this.handle)); +#pragma warning restore CA2000 // Dispose objects before losing scope + } + /// /// Initializes a new instance of the class. /// From c6025bba259ea5d5e8251ed2f5dccf37de08fa69 Mon Sep 17 00:00:00 2001 From: Brent Allen Date: Tue, 8 Oct 2019 10:54:19 -0700 Subject: [PATCH 10/16] Added note on default parameters to standards.md --- docs/standards.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/standards.md b/docs/standards.md index 20d56ace9..b9efd34ce 100644 --- a/docs/standards.md +++ b/docs/standards.md @@ -46,6 +46,7 @@ allow the reader to quickly and easily tell the difference between internal and there is nothing the user can do to resolve the issue, then it is acceptable to crash. If there is action the user could take (not including an application restart or PC reboot), then returning an error specific to the recovery action is acceptable. +* Don't use default parameters in public APIs (C++/C#) ## Clang-format From 972324a9c8d4c3edc473785c8d8d8c244e9c0028 Mon Sep 17 00:00:00 2001 From: Brent Allen Date: Tue, 8 Oct 2019 13:10:24 -0700 Subject: [PATCH 11/16] Tests passing, and build config --- docs/releasing.md | 109 +++++++++--------- include/k4arecord/record.h | 13 +-- ...re.Kinect.Sensor.Examples.Recording.csproj | 42 ++++++- src/csharp/Examples/Recording/Program.cs | 108 ++++++++++------- ...re.Kinect.Sensor.Examples.WPFViewer.csproj | 4 +- ...ure.Kinect.Sensor.Examples.WinForms.csproj | 4 +- src/csharp/K4a.sln | 24 ++-- ...icrosoft.Azure.Kinect.Sensor.Record.csproj | 36 +++++- src/csharp/Record/NativeMethods.cs | 4 +- src/csharp/Record/Playback.cs | 7 +- .../Tests/Record.UnitTests/CaptureTests.cs | 32 +++-- ...zure.Kinect.Sensor.Record.UnitTests.csproj | 4 + src/record/sdk/record.cpp | 4 +- 13 files changed, 254 insertions(+), 137 deletions(-) diff --git a/docs/releasing.md b/docs/releasing.md index b5fbda4a4..8c57bde6a 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 | 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.1 (symlink) | | | :white_check_mark: | | -linux-ubuntu/x64/release/libk4a.so.1.1.0 | | | :white_check_mark: | | -linux-ubuntu/x64/release/libk4arecord.so (symlink) | | | | :white_check_mark: | -linux-ubuntu/x64/release/libk4arecord.so.1.1 (symlink) | | | :white_check_mark: | | -linux-ubuntu/x64/release/libk4arecord.so.1.1.0 | | | :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.1 (symlink) | | | :white_check_mark: | | +linux-ubuntu/x64/release/libk4a.so.1.1.0 | | | :white_check_mark: | | +linux-ubuntu/x64/release/libk4arecord.so (symlink) | | | | :white_check_mark: | +linux-ubuntu/x64/release/libk4arecord.so.1.1 (symlink) | | | :white_check_mark: | | +linux-ubuntu/x64/release/libk4arecord.so.1.1.0 | | | :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/record.h b/include/k4arecord/record.h index 137c2409c..90d1ab654 100644 --- a/include/k4arecord/record.h +++ b/include/k4arecord/record.h @@ -462,7 +462,6 @@ K4ARECORD_EXPORT k4a_result_t k4a_record_flush(k4a_record_t recording_handle); */ K4ARECORD_EXPORT void k4a_record_close(k4a_record_t recording_handle); - /** Sets and clears the callback function to receive debug messages from the Azure Kinect record and playback APIs. * * \param message_cb @@ -492,11 +491,11 @@ K4ARECORD_EXPORT void k4a_record_close(k4a_record_t recording_handle); * \remarks * Logging provided via this API is independent of the logging controlled by the environmental variable controls \p * K4A_ENABLE_LOG_TO_STDOUT, K4A_RECORD_ENABLE_LOG_TO_A_FILE, and K4A_LOG_LEVEL. However there is a slight change in - * default behavior when using this function. By default, when k4a_record_set_debug_message_handler() has not been used to - * register a message callback, the default for environmental variable controls is to send debug messages as if + * default behavior when using this function. By default, when k4a_record_set_debug_message_handler() has not been used + * to register a message callback, the default for environmental variable controls is to send debug messages as if * K4A_ENABLE_LOG_TO_STDOUT=1 were set. If k4a_record_set_debug_message_handler registers a callback function before - * k4a_record_create() or k4a_playback_create() is called, then the default for environmental controls is as if K4A_ENABLE_LOG_TO_STDOUT=0 was - * specified. Physically specifying the environmental control will override the default. + * k4a_record_create() or k4a_playback_create() is called, then the default for environmental controls is as if + * K4A_ENABLE_LOG_TO_STDOUT=0 was specified. Physically specifying the environmental control will override the default. * * \xmlonly * @@ -507,8 +506,8 @@ K4ARECORD_EXPORT void k4a_record_close(k4a_record_t recording_handle); * \endxmlonly */ K4ARECORD_EXPORT k4a_result_t k4a_record_set_debug_message_handler(k4a_logging_message_cb_t *message_cb, - void *message_cb_context, - k4a_log_level_t min_level); + void *message_cb_context, + k4a_log_level_t min_level); /** * @} */ diff --git a/src/csharp/Examples/Recording/Microsoft.Azure.Kinect.Sensor.Examples.Recording.csproj b/src/csharp/Examples/Recording/Microsoft.Azure.Kinect.Sensor.Examples.Recording.csproj index f21e48217..27d7bbb66 100644 --- a/src/csharp/Examples/Recording/Microsoft.Azure.Kinect.Sensor.Examples.Recording.csproj +++ b/src/csharp/Examples/Recording/Microsoft.Azure.Kinect.Sensor.Examples.Recording.csproj @@ -1,9 +1,15 @@  + Exe - netcoreapp2.2 - dotnetrecording + netcoreapp2.1 + dotnetrecording + + x64;x86 + false + ..\..\AzureKinectSensorSDK.ruleset + $(BaseOutputPath)\$(AssemblyName)\ @@ -11,4 +17,36 @@ + + + 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 index 077b7e5a3..e91b43cd1 100644 --- a/src/csharp/Examples/Recording/Program.cs +++ b/src/csharp/Examples/Recording/Program.cs @@ -11,69 +11,91 @@ static void Main(string[] args) { int frame = 0; - Console.WriteLine("Recording from device."); - - DeviceConfiguration configuration = new DeviceConfiguration() + if (args.Length < 1) { - CameraFPS = FPS.FPS30, - ColorFormat = ImageFormat.ColorMJPG, - ColorResolution = ColorResolution.R720p, - DepthMode = DepthMode.NFOV_2x2Binned, - SynchronizedImagesOnly = true - }; - - using (Device device = Device.Open()) - using (Record recording = Record.Create(@"output.mkv", device, configuration)) + Console.WriteLine("Please specify the name of an .mkv output file."); + return; + } + + string path = args[0]; + + try { - - device.StartCameras(configuration); - device.StartImu(); + Console.WriteLine($"Recording from device to \"{path}\"."); - recording.AddImuTrack(); - recording.WriteHeader(); + DeviceConfiguration configuration = new DeviceConfiguration() + { + CameraFPS = FPS.FPS30, + ColorFormat = ImageFormat.ColorMJPG, + ColorResolution = ColorResolution.R720p, + DepthMode = DepthMode.NFOV_2x2Binned, + SynchronizedImagesOnly = true + }; - for (frame = 0; frame < 100; frame++) + using (Device device = Device.Open()) + using (Record recording = Record.Create(path, device, configuration)) { - using (Capture capture = device.GetCapture()) + + device.StartCameras(configuration); + device.StartImu(); + + recording.AddImuTrack(); + recording.WriteHeader(); + + for (frame = 0; frame < 100; frame++) { - recording.WriteCapture(capture); - Console.WriteLine($"Wrote capture ({capture.Color.DeviceTimestamp})"); - try + using (Capture capture = device.GetCapture()) { - while (true) + recording.WriteCapture(capture); + Console.WriteLine($"Wrote capture ({capture.Color.DeviceTimestamp})"); + try { - // Throws TimeoutException when Imu sample is not available - ImuSample sample = device.GetImuSample(TimeSpan.Zero); + while (true) + { + // Throws TimeoutException when Imu sample is not available + ImuSample sample = device.GetImuSample(TimeSpan.Zero); - recording.WriteImuSample(sample); - Console.WriteLine($"Wrote imu ({sample.AccelerometerTimestamp})"); + recording.WriteImuSample(sample); + Console.WriteLine($"Wrote imu ({sample.AccelerometerTimestamp})"); + } } - }catch (TimeoutException) - { + 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}"); + Console.WriteLine($"Wrote {frame} frames to output.mkv"); - for (int i = 0; i < playback.TrackCount; i++) + using (Playback playback = Playback.Open(@"output.mkv")) { - string name = playback.GetTrackName(i); - string codecId = playback.GetTrackCodecId(name); + 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)})"); + 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}"); + } } - Capture capture; - while ( null != (capture = playback.GetNextCapture())) + + } catch (AzureKinectException exception) + { + Console.WriteLine(exception.ToString()); + Console.WriteLine(); + Console.WriteLine("Azure Kinect log messages:"); + foreach (LogMessage m in exception.LogMessages) { - Console.WriteLine($"Color timestamp: {capture.Color.DeviceTimestamp} Depth timestamp: {capture.Depth.DeviceTimestamp}"); + 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 b0094b756..20604e144 100644 --- a/src/csharp/K4a.sln +++ b/src/csharp/K4a.sln @@ -44,7 +44,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Azure.Kinect.Sens 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("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Azure.Kinect.Sensor.Record.UnitTests", "Tests\Record.UnitTests\Microsoft.Azure.Kinect.Sensor.Record.UnitTests.csproj", "{4CAEC910-CEC0-41CD-8E47-AF20F5570203}" +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 @@ -156,18 +156,16 @@ Global {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|Any CPU - {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Debug|Any CPU.Build.0 = Debug|Any CPU - {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Debug|x64.ActiveCfg = Debug|Any CPU - {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Debug|x64.Build.0 = Debug|Any CPU - {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Debug|x86.ActiveCfg = Debug|Any CPU - {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Debug|x86.Build.0 = Debug|Any CPU - {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Release|Any CPU.ActiveCfg = Release|Any CPU - {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Release|Any CPU.Build.0 = Release|Any CPU - {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Release|x64.ActiveCfg = Release|Any CPU - {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Release|x64.Build.0 = Release|Any CPU - {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Release|x86.ActiveCfg = Release|Any CPU - {568BBB67-4EE0-4A0D-AD69-5D10386E2D40}.Release|x86.Build.0 = Release|Any CPU + {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|Any CPU diff --git a/src/csharp/Record/Microsoft.Azure.Kinect.Sensor.Record.csproj b/src/csharp/Record/Microsoft.Azure.Kinect.Sensor.Record.csproj index 298872f62..2800ddc00 100644 --- a/src/csharp/Record/Microsoft.Azure.Kinect.Sensor.Record.csproj +++ b/src/csharp/Record/Microsoft.Azure.Kinect.Sensor.Record.csproj @@ -1,11 +1,45 @@  - + + netstandard2.0 + latest + + false + ..\AzureKinectSensorSDK.ruleset + $(BaseOutputPath)\$(AssemblyName)\ + + + + 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 index 7d3ed31c9..c1ee0995f 100644 --- a/src/csharp/Record/NativeMethods.cs +++ b/src/csharp/Record/NativeMethods.cs @@ -155,11 +155,11 @@ public static extern k4a_stream_result_t k4a_playback_get_previous_capture(k4a_p [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); + [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); + [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, diff --git a/src/csharp/Record/Playback.cs b/src/csharp/Record/Playback.cs index b18d65aea..40204c701 100644 --- a/src/csharp/Record/Playback.cs +++ b/src/csharp/Record/Playback.cs @@ -536,8 +536,9 @@ public ImuSample GetNextImuSample() { 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, out NativeMethods.k4a_imu_sample_t imu_sample)) + switch (NativeMethods.k4a_playback_get_next_imu_sample(this.handle, imu_sample)) { case NativeMethods.k4a_stream_result_t.K4A_STREAM_RESULT_EOF: return null; @@ -574,7 +575,9 @@ public ImuSample GetPreviousImuSample() throw new ObjectDisposedException(nameof(Playback)); } - switch (NativeMethods.k4a_playback_get_previous_imu_sample(this.handle, out NativeMethods.k4a_imu_sample_t imu_sample)) + 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; diff --git a/src/csharp/Tests/Record.UnitTests/CaptureTests.cs b/src/csharp/Tests/Record.UnitTests/CaptureTests.cs index a2e4717da..39ab2e163 100644 --- a/src/csharp/Tests/Record.UnitTests/CaptureTests.cs +++ b/src/csharp/Tests/Record.UnitTests/CaptureTests.cs @@ -40,7 +40,7 @@ public void Test1() using (Record record = Record.Create(this.recordingPath, null, deviceConfiguration)) { - + 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"); @@ -120,8 +120,8 @@ public void Test1() using (Capture c = playback.GetNextCapture()) { - - Assert.AreEqual(25.0f, c.Temperature); + // Not captured in recording + // Assert.AreEqual(25.0f, c.Temperature); Assert.AreEqual(ImageFormat.ColorNV12, c.Color.Format); Assert.AreEqual(1280, c.Color.WidthPixels); @@ -131,10 +131,16 @@ public void Test1() Assert.AreEqual(TimeSpan.FromSeconds(timeStamp) + deviceConfiguration.DepthDelayOffColor, c.Depth.DeviceTimestamp); Assert.AreEqual(TimeSpan.FromSeconds(timeStamp) + deviceConfiguration.DepthDelayOffColor, c.IR.DeviceTimestamp); - Assert.AreEqual(TimeSpan.FromMilliseconds(12), c.Color.Exposure); - Assert.AreEqual(100, c.Color.ISOSpeed); + // 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); - Assert.AreEqual(2, c.Color.WhiteBalance); + + // Not captured in recording + // Assert.AreEqual(2, c.Color.WhiteBalance); } for (int y = 0; y < 10; y++) @@ -149,7 +155,14 @@ public void Test1() }; ImuSample readSample = playback.GetNextImuSample(); - Assert.AreEqual(imuSample, readSample); + + 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]; @@ -159,7 +172,7 @@ public void Test1() } using (DataBlock videoBlock = playback.GetNextDataBlock("CUSTOM_VIDEO")) { - Assert.AreEqual(customData, videoBlock); + Assert.AreEqual(customData, videoBlock.Buffer); Assert.AreEqual(TimeSpan.FromSeconds(timeStamp), videoBlock.DeviceTimestamp); } @@ -169,12 +182,11 @@ public void Test1() } using (DataBlock subtitleBlock = playback.GetNextDataBlock("CUSTOM_SUBTITLE")) { - Assert.AreEqual(customData, subtitleBlock); + Assert.AreEqual(customData, subtitleBlock.Buffer); Assert.AreEqual(TimeSpan.FromSeconds(timeStamp), subtitleBlock.DeviceTimestamp); } } } - Assert.Pass(); } } } \ 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 index 77e26d488..4f7a9e27f 100644 --- 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 @@ -11,6 +11,10 @@ ..\..\AzureKinectSensorSDK.ruleset $(BaseOutputPath)\$(AssemblyName)\ + + + false + diff --git a/src/record/sdk/record.cpp b/src/record/sdk/record.cpp index aed9f109b..10b7b771b 100644 --- a/src/record/sdk/record.cpp +++ b/src/record/sdk/record.cpp @@ -15,8 +15,8 @@ using namespace k4arecord; using namespace LIBMATROSKA_NAMESPACE; k4a_result_t k4a_record_set_debug_message_handler(k4a_logging_message_cb_t *message_cb, - void *message_cb_context, - k4a_log_level_t min_level) + void *message_cb_context, + k4a_log_level_t min_level) { return logger_register_message_callback(message_cb, message_cb_context, min_level); } From 0d50febc39904294adfe081cbfaed548c9972704 Mon Sep 17 00:00:00 2001 From: Brent Allen Date: Tue, 8 Oct 2019 14:58:47 -0700 Subject: [PATCH 12/16] Cleaned up C# style --- src/csharp/Record/RecordLogger.cs | 69 +++++++++++-------- src/csharp/SDK/Allocator.cs | 2 + src/csharp/SDK/Device.cs | 43 ++++++------ src/csharp/SDK/GlobalSuppressions.cs | 8 +++ src/csharp/SDK/ILoggingProvider.cs | 30 ++++++++ src/csharp/SDK/Logger.cs | 67 +++++++++--------- src/csharp/SDK/Native/LoggingTracer.cs | 8 ++- src/csharp/SDK/Native/NativeMethods.cs | 2 +- .../{CaptureTests.cs => LoopbackTests.cs} | 62 ++++++++++------- 9 files changed, 182 insertions(+), 109 deletions(-) create mode 100644 src/csharp/SDK/GlobalSuppressions.cs create mode 100644 src/csharp/SDK/ILoggingProvider.cs rename src/csharp/Tests/Record.UnitTests/{CaptureTests.cs => LoopbackTests.cs} (85%) diff --git a/src/csharp/Record/RecordLogger.cs b/src/csharp/Record/RecordLogger.cs index ea644c6f8..5cd50e26b 100644 --- a/src/csharp/Record/RecordLogger.cs +++ b/src/csharp/Record/RecordLogger.cs @@ -1,4 +1,10 @@ -using System; +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +//------------------------------------------------------------------------------ +using System; using System.Collections.Generic; using System.Diagnostics; using System.Text; @@ -12,39 +18,15 @@ public static class RecordLogger { private static readonly object SyncRoot = new object(); private static readonly NativeMethods.k4a_logging_message_cb_t DebugMessageHandler = OnDebugMessage; + private static readonly RecordLoggerProvider LoggerProvider = new RecordLoggerProvider(); private static bool isInitialized; - private static event Action LogMessageHandlers; - - private class RecordLoggerProvider : ILoggingProvider - { - public event Action LogMessage - { - add - { - RecordLogger.LogMessage += value; - } - remove - { - RecordLogger.LogMessage -= value; - } - } - } - - private readonly static RecordLoggerProvider loggerProvider = new RecordLoggerProvider(); - - public static ILoggingProvider LogProvider - { - get - { - return RecordLogger.loggerProvider; - } - } - +#pragma warning disable CA1003 // Use generic event handler instances /// /// Occurs when the Azure Kinect Sensor Record and Playback SDK delivers a debug message. /// public static event Action LogMessage +#pragma warning restore CA1003 // Use generic event handler instances { add { @@ -68,6 +50,19 @@ public static event Action LogMessage } } + private static event Action LogMessageHandlers; + + /// + /// Gets the interface for reading log messages. + /// + public static ILoggingProvider LogProvider + { + get + { + return RecordLogger.LoggerProvider; + } + } + /// /// Initializes the class to begin receiving messages from the Azure Kinect Sensor SDK. /// @@ -138,5 +133,23 @@ private static void CurrentDomain_Exit(object sender, EventArgs e) Trace.WriteLine("Failed to close the debug message handler"); } } + + private class RecordLoggerProvider : ILoggingProvider + { + public event Action LogMessage + { + add + { + RecordLogger.LogMessage += value; + } + + remove + { + RecordLogger.LogMessage -= value; + } + } + + public string ProviderName => "Azure Kinect Recording SDK"; + } } } diff --git a/src/csharp/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/Device.cs b/src/csharp/SDK/Device.cs index 263e3b88c..6d77c6c24 100644 --- a/src/csharp/SDK/Device.cs +++ b/src/csharp/SDK/Device.cs @@ -166,7 +166,6 @@ public HardwareVersion Version } } } - /// /// Gets the native handle. /// @@ -193,27 +192,6 @@ public IntPtr Handle } } - /// - /// 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; - } - } - /// /// Gets the number of currently connected devices. /// @@ -236,6 +214,27 @@ public static Device Open(int index = 0) return new Device(handle); } + /// + /// 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; + } + } + /// /// Gets the calibration of the device. /// diff --git a/src/csharp/SDK/GlobalSuppressions.cs b/src/csharp/SDK/GlobalSuppressions.cs new file mode 100644 index 000000000..2f6f2f7b7 --- /dev/null +++ b/src/csharp/SDK/GlobalSuppressions.cs @@ -0,0 +1,8 @@ + +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Maintainability", "CA1508:Avoid dead conditional code", Justification = "", Scope = "member", Target = "~M:Microsoft.Azure.Kinect.Sensor.Device.GetCapture(System.TimeSpan)~Microsoft.Azure.Kinect.Sensor.Capture")] + diff --git a/src/csharp/SDK/ILoggingProvider.cs b/src/csharp/SDK/ILoggingProvider.cs new file mode 100644 index 000000000..0a03df569 --- /dev/null +++ b/src/csharp/SDK/ILoggingProvider.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +//------------------------------------------------------------------------------ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.Azure.Kinect.Sensor +{ + /// + /// An interface for trace logging providers from the native SDK. + /// + public interface ILoggingProvider + { +#pragma warning disable CA1003 // Use generic event handler instances + /// + /// Occurs when the native SDK delivers a debug message. + /// + event Action LogMessage; +#pragma warning restore CA1003 // Use generic event handler instances + + /// + /// Gets the name of the layer providing the messages. + /// + string ProviderName { get; } + } +} diff --git a/src/csharp/SDK/Logger.cs b/src/csharp/SDK/Logger.cs index c289eeae6..a919fa087 100644 --- a/src/csharp/SDK/Logger.cs +++ b/src/csharp/SDK/Logger.cs @@ -9,12 +9,6 @@ namespace Microsoft.Azure.Kinect.Sensor { - - public interface ILoggingProvider - { - event Action LogMessage; - } - /// /// The Azure Kinect logging system. Enables access to the debug messages from the Azure Kinect device. /// @@ -22,39 +16,15 @@ public static class Logger { private static readonly object SyncRoot = new object(); private static readonly NativeMethods.k4a_logging_message_cb_t DebugMessageHandler = OnDebugMessage; + private static readonly LoggerProvider LoggerProviderValue = new LoggerProvider(); private static bool isInitialized; - private static event Action LogMessageHandlers; - - private class LoggerProvider : ILoggingProvider - { - public event Action LogMessage - { - add - { - Logger.LogMessage += value; - } - remove - { - Logger.LogMessage -= value; - } - } - } - - private readonly static LoggerProvider loggerProvider = new LoggerProvider(); - - public static ILoggingProvider LogProvider - { - get - { - return Logger.loggerProvider; - } - } - +#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 { @@ -78,6 +48,19 @@ public static event Action LogMessage } } + private static event Action LogMessageHandlers; + + /// + /// Gets the interface for reading log messages. + /// + public static ILoggingProvider LogProvider + { + get + { + return Logger.LoggerProviderValue; + } + } + /// /// Initializes the class to begin receiving messages from the Azure Kinect Sensor SDK. /// @@ -148,5 +131,23 @@ private static void CurrentDomain_Exit(object sender, EventArgs e) Trace.WriteLine("Failed to close the debug message handler"); } } + + private class LoggerProvider : ILoggingProvider + { + public event Action LogMessage + { + add + { + Logger.LogMessage += value; + } + + remove + { + Logger.LogMessage -= value; + } + } + + public string ProviderName => "Azure Kinect SDK"; + } } } diff --git a/src/csharp/SDK/Native/LoggingTracer.cs b/src/csharp/SDK/Native/LoggingTracer.cs index 71291ad7c..9ce9d9452 100644 --- a/src/csharp/SDK/Native/LoggingTracer.cs +++ b/src/csharp/SDK/Native/LoggingTracer.cs @@ -17,14 +17,13 @@ namespace Microsoft.Azure.Kinect.Sensor public class LoggingTracer : IDisposable { private readonly int threadId; + private readonly LogLevel minLevel; private bool disposed; private List messages; private ILoggingProvider[] loggingProviders; - private LogLevel minLevel; - /// /// Initializes a new instance of the class. /// @@ -33,6 +32,11 @@ public LoggingTracer() { } + /// + /// Initializes a new instance of the class. + /// + /// Minimum level of messages to capture. + /// Set of logging providers to capture from. public LoggingTracer(LogLevel minLevel, params ILoggingProvider[] loggingProvider) { this.messages = new List(); diff --git a/src/csharp/SDK/Native/NativeMethods.cs b/src/csharp/SDK/Native/NativeMethods.cs index e8e8a9a41..1ce4a0bd3 100644 --- a/src/csharp/SDK/Native/NativeMethods.cs +++ b/src/csharp/SDK/Native/NativeMethods.cs @@ -374,7 +374,7 @@ public static extern k4a_result_t k4a_set_debug_message_handler( [DllImport("k4arecord", CallingConvention = k4aCallingConvention, CharSet = CharSet.Ansi)] [NativeReference] - public static extern k4a_result_t k4a_record_create(string path, k4a_device_t device, k4a_device_configuration_t deviceConfiguration, out k4a_record_t handle); + public static extern k4a_result_t k4a_record_create([MarshalAs(UnmanagedType.LPStr)] string path, k4a_device_t device, k4a_device_configuration_t deviceConfiguration, out k4a_record_t handle); [DllImport("k4arecord", CallingConvention = k4aCallingConvention)] [NativeReference] diff --git a/src/csharp/Tests/Record.UnitTests/CaptureTests.cs b/src/csharp/Tests/Record.UnitTests/LoopbackTests.cs similarity index 85% rename from src/csharp/Tests/Record.UnitTests/CaptureTests.cs rename to src/csharp/Tests/Record.UnitTests/LoopbackTests.cs index 39ab2e163..2190ef195 100644 --- a/src/csharp/Tests/Record.UnitTests/CaptureTests.cs +++ b/src/csharp/Tests/Record.UnitTests/LoopbackTests.cs @@ -1,29 +1,41 @@ -using System; -using System.Threading; +using System; using Microsoft.Azure.Kinect.Sensor; using Microsoft.Azure.Kinect.Sensor.Record; using NUnit.Framework; namespace Tests { - public class Tests + /// + /// Loopback Tests write to a recording, and then read the recording back to verify the API. + /// + public class LoopbackTests { - string recordingPath; + private string recordingPath; + + /// + /// Allocate a path for the recording. + /// [SetUp] public void Setup() { - recordingPath = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "testfile.mkv"); + this.recordingPath = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "testfile.mkv"); } + /// + /// Delete the temporary recording. + /// [TearDown] public void TearDown() { - System.IO.File.Delete(recordingPath); + 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 Test1() + public void LoopbackTest1() { DeviceConfiguration deviceConfiguration = new DeviceConfiguration() { @@ -34,15 +46,17 @@ public void Test1() DepthMode = DepthMode.NFOV_2x2Binned, DisableStreamingIndicator = true, SuboridinateDelayOffMaster = TimeSpan.FromMilliseconds(456), - SynchronizedImagesOnly = true, - WiredSyncMode = WiredSyncMode.Subordinate + SynchronizedImagesOnly = true, + WiredSyncMode = WiredSyncMode.Subordinate, }; +#pragma warning disable CA1508 // Avoid dead conditional code using (Record record = Record.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.AddCustomSubtitleTrack("CUSTOM_SUBTITLE", "S_CUSTOM1", new byte[] { 4, 5, 6, 7 }, new RecordSubtitleSettings() { HighFrequencyData = false }); record.AddTag("MyTag1", "one"); record.AddTag("MyTag2", "two"); @@ -50,9 +64,11 @@ public void Test1() for (int i = 0; i < 10; i++) { - double timeStamp = 10.0 + i * 1.0; + 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); @@ -80,8 +96,8 @@ public void Test1() { 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), + AccelerometerTimestamp = TimeSpan.FromSeconds(timeStamp + (0.1 * y)), + GyroTimestamp = TimeSpan.FromSeconds(timeStamp + (0.1 * y)), Temperature = 26.0f, }; @@ -93,20 +109,21 @@ public void Test1() { 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(recordingPath)) + using (Playback playback = Playback.Open(this.recordingPath)) { Assert.IsTrue(playback.CheckTrackExists("CUSTOM_VIDEO")); Assert.IsTrue(playback.CheckTrackExists("CUSTOM_SUBTITLE")); @@ -115,28 +132,25 @@ public void Test1() for (int i = 0; i < 10; i++) { - double timeStamp = 10.0 + i * 1.0; + 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 @@ -155,7 +169,7 @@ public void Test1() }; ImuSample readSample = playback.GetNextImuSample(); - + Assert.AreEqual(imuSample.AccelerometerSample, readSample.AccelerometerSample); Assert.AreEqual(imuSample.GyroSample, readSample.GyroSample); Assert.AreEqual(imuSample.AccelerometerTimestamp, readSample.AccelerometerTimestamp); @@ -170,6 +184,7 @@ public void Test1() { customData[x] = (byte)(i + x); } + using (DataBlock videoBlock = playback.GetNextDataBlock("CUSTOM_VIDEO")) { Assert.AreEqual(customData, videoBlock.Buffer); @@ -180,6 +195,7 @@ public void Test1() { customData[x] = (byte)(i + x + 1); } + using (DataBlock subtitleBlock = playback.GetNextDataBlock("CUSTOM_SUBTITLE")) { Assert.AreEqual(customData, subtitleBlock.Buffer); From 2b49c265a813b860512cb1eab9cc8eb1aeb29d62 Mon Sep 17 00:00:00 2001 From: Brent Allen Date: Wed, 9 Oct 2019 11:02:54 -0700 Subject: [PATCH 13/16] Style cleanup --- src/csharp/Examples/Recording/Program.cs | 11 +- src/csharp/Record/DataBlock.cs | 32 ++- .../AzureKinectAddAttachmentException.cs | 12 +- ...reKinectAddCustomSubtitleTrackException.cs | 12 +- ...AzureKinectAddCustomVideoTrackException.cs | 12 +- .../AzureKinectAddImuTrackException.cs | 10 +- .../Exceptions/AzureKinectAddTagException.cs | 2 +- .../AzureKinectCreateRecordingException.cs | 6 +- .../Exceptions/AzureKinectFlushException.cs | 12 +- .../AzureKinectGetCalibrationException.cs | 10 +- .../AzureKinectGetCaptureException.cs | 10 +- .../AzureKinectGetDataBlockException.cs | 10 +- .../AzureKinectGetImuSampleException.cs | 10 +- .../AzureKinectGetRawCalibrationException.cs | 10 +- .../Exceptions/AzureKinectGetTagException.cs | 12 +- ...zureKinectGetTrackCodecContextException.cs | 12 +- .../AzureKinectGetTrackNameException.cs | 12 +- .../AzureKinectOpenPlaybackException.cs | 10 +- .../Exceptions/AzureKinectRecordException.cs | 20 +- .../Exceptions/AzureKinectSeekException.cs | 12 +- .../AzureKinectSetColorConversionException.cs | 10 +- ...ureKinectTrackGetVideoSettingsException.cs | 10 +- .../AzureKinectWriteCaptureException.cs | 12 +- ...zureKinectWriteCustomTrackDataException.cs | 12 +- .../AzureKinectWriteHeaderException.cs | 12 +- .../AzureKinectWriteImuSampleException.cs | 10 +- src/csharp/Record/NativeMethods.cs | 95 +++---- src/csharp/Record/Playback.cs | 234 +++++++++--------- src/csharp/Record/PlaybackSeekOrigin.cs | 26 +- src/csharp/Record/RecordConfiguration.cs | 83 ++++++- src/csharp/Record/RecordLogger.cs | 10 +- src/csharp/Record/RecordSubtitleSettings.cs | 19 +- src/csharp/Record/RecordVideoSettings.cs | 29 ++- src/csharp/Record/{Record.cs => Recorder.cs} | 72 +++--- src/csharp/SDK/Device.cs | 47 ++-- src/csharp/SDK/GlobalSuppressions.cs | 8 - src/csharp/SDK/Native/NativeMethods.cs | 34 --- .../Tests/Record.UnitTests/LoopbackTests.cs | 6 +- 38 files changed, 584 insertions(+), 382 deletions(-) rename src/csharp/Record/{Record.cs => Recorder.cs} (92%) delete mode 100644 src/csharp/SDK/GlobalSuppressions.cs diff --git a/src/csharp/Examples/Recording/Program.cs b/src/csharp/Examples/Recording/Program.cs index e91b43cd1..f7d4898ed 100644 --- a/src/csharp/Examples/Recording/Program.cs +++ b/src/csharp/Examples/Recording/Program.cs @@ -31,22 +31,21 @@ static void Main(string[] args) DepthMode = DepthMode.NFOV_2x2Binned, SynchronizedImagesOnly = true }; - using (Device device = Device.Open()) - using (Record recording = Record.Create(path, device, configuration)) + using (Recorder recorder = Recorder.Create(path, device, configuration)) { device.StartCameras(configuration); device.StartImu(); - recording.AddImuTrack(); - recording.WriteHeader(); + recorder.AddImuTrack(); + recorder.WriteHeader(); for (frame = 0; frame < 100; frame++) { using (Capture capture = device.GetCapture()) { - recording.WriteCapture(capture); + recorder.WriteCapture(capture); Console.WriteLine($"Wrote capture ({capture.Color.DeviceTimestamp})"); try { @@ -55,7 +54,7 @@ static void Main(string[] args) // Throws TimeoutException when Imu sample is not available ImuSample sample = device.GetImuSample(TimeSpan.Zero); - recording.WriteImuSample(sample); + recorder.WriteImuSample(sample); Console.WriteLine($"Wrote imu ({sample.AccelerometerTimestamp})"); } } diff --git a/src/csharp/Record/DataBlock.cs b/src/csharp/Record/DataBlock.cs index 5fdc0a52c..a9c7e2cda 100644 --- a/src/csharp/Record/DataBlock.cs +++ b/src/csharp/Record/DataBlock.cs @@ -1,12 +1,19 @@ -using System; -using System.Collections.Generic; -using System.Globalization; +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +//------------------------------------------------------------------------------ +using System; +using System.Buffers; using System.Runtime.InteropServices; -using System.Text; namespace Microsoft.Azure.Kinect.Sensor.Record { - public class DataBlock : IDisposable + /// + /// 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; @@ -16,12 +23,19 @@ public class DataBlock : IDisposable 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; } - public byte[] Buffer + /// + /// Gets the memory with the custom data. + /// + public Memory Memory { get { @@ -37,7 +51,7 @@ public byte[] Buffer 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) @@ -55,6 +69,9 @@ public byte[] Buffer } } + /// + /// Gets the device timestamp associated with the data. + /// public TimeSpan DeviceTimestamp { get @@ -73,7 +90,6 @@ public TimeSpan DeviceTimestamp } } - /// public void Dispose() { diff --git a/src/csharp/Record/Exceptions/AzureKinectAddAttachmentException.cs b/src/csharp/Record/Exceptions/AzureKinectAddAttachmentException.cs index 3dd850417..aacf0d6a6 100644 --- a/src/csharp/Record/Exceptions/AzureKinectAddAttachmentException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectAddAttachmentException.cs @@ -1,13 +1,17 @@ -using System; +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +//------------------------------------------------------------------------------ +using System; using System.Collections.Generic; using System.Runtime.Serialization; -using System.Text; namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions { - /// - /// Represents errors that occur when adding an attachment to a recording + /// Represents errors that occur when adding an attachment to a recording. /// [Serializable] public class AzureKinectAddAttachmentException : AzureKinectRecordException diff --git a/src/csharp/Record/Exceptions/AzureKinectAddCustomSubtitleTrackException.cs b/src/csharp/Record/Exceptions/AzureKinectAddCustomSubtitleTrackException.cs index b75e5fe46..12527430e 100644 --- a/src/csharp/Record/Exceptions/AzureKinectAddCustomSubtitleTrackException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectAddCustomSubtitleTrackException.cs @@ -1,13 +1,17 @@ -using System; +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +//------------------------------------------------------------------------------ +using System; using System.Collections.Generic; using System.Runtime.Serialization; -using System.Text; namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions { - /// - /// Represents errors that occur when adding a custom subtitle track + /// Represents errors that occur when adding a custom subtitle track. /// [Serializable] public class AzureKinectAddCustomSubtitleTrackException : AzureKinectRecordException diff --git a/src/csharp/Record/Exceptions/AzureKinectAddCustomVideoTrackException.cs b/src/csharp/Record/Exceptions/AzureKinectAddCustomVideoTrackException.cs index efa5be47d..aa59301cd 100644 --- a/src/csharp/Record/Exceptions/AzureKinectAddCustomVideoTrackException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectAddCustomVideoTrackException.cs @@ -1,13 +1,17 @@ -using System; +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +//------------------------------------------------------------------------------ +using System; using System.Collections.Generic; using System.Runtime.Serialization; -using System.Text; namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions { - /// - /// Represents errors that occur when adding a custom video track + /// Represents errors that occur when adding a custom video track. /// [Serializable] public class AzureKinectAddCustomVideoTrackException : AzureKinectRecordException diff --git a/src/csharp/Record/Exceptions/AzureKinectAddImuTrackException.cs b/src/csharp/Record/Exceptions/AzureKinectAddImuTrackException.cs index 161c8dde9..3fadd2128 100644 --- a/src/csharp/Record/Exceptions/AzureKinectAddImuTrackException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectAddImuTrackException.cs @@ -1,11 +1,15 @@ -using System; +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +//------------------------------------------------------------------------------ +using System; using System.Collections.Generic; using System.Runtime.Serialization; -using System.Text; namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions { - /// /// Represents errors that occur when adding an IMU track to a recording. /// diff --git a/src/csharp/Record/Exceptions/AzureKinectAddTagException.cs b/src/csharp/Record/Exceptions/AzureKinectAddTagException.cs index 43d4f259e..070d9ca06 100644 --- a/src/csharp/Record/Exceptions/AzureKinectAddTagException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectAddTagException.cs @@ -11,7 +11,7 @@ namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions { /// - /// Represents errors that occur when adding a tag to a recording + /// Represents errors that occur when adding a tag to a recording. /// [Serializable] public class AzureKinectAddTagException : AzureKinectRecordException diff --git a/src/csharp/Record/Exceptions/AzureKinectCreateRecordingException.cs b/src/csharp/Record/Exceptions/AzureKinectCreateRecordingException.cs index 58506b54c..db66af19c 100644 --- a/src/csharp/Record/Exceptions/AzureKinectCreateRecordingException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectCreateRecordingException.cs @@ -7,7 +7,6 @@ using System; using System.Collections.Generic; using System.Runtime.Serialization; -using Microsoft.Azure.Kinect.Sensor.Record; namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions { @@ -84,13 +83,12 @@ protected AzureKinectCreateRecordingException(string message, ICollection if the result of the function /// is not a success. /// - /// File name of the create + /// File name of the create. /// The native function to call. /// The type of result to expect from the function call. internal static void ThrowIfNotSuccess(string fileName, Func function) where T : System.Enum { - using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider)) { T result = function(); @@ -105,7 +103,7 @@ internal static void ThrowIfNotSuccess(string fileName, Func function) /// Throws an if the result of the function /// is not a success. /// - /// File name of the create + /// 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. diff --git a/src/csharp/Record/Exceptions/AzureKinectFlushException.cs b/src/csharp/Record/Exceptions/AzureKinectFlushException.cs index c0021f44d..e23177971 100644 --- a/src/csharp/Record/Exceptions/AzureKinectFlushException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectFlushException.cs @@ -1,13 +1,17 @@ -using System; +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +//------------------------------------------------------------------------------ +using System; using System.Collections.Generic; using System.Runtime.Serialization; -using System.Text; namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions { - /// - /// Represents errors that occur when an error occurs during flushing + /// Represents errors that occur when an error occurs during flushing. /// [Serializable] public class AzureKinectFlushException : AzureKinectRecordException diff --git a/src/csharp/Record/Exceptions/AzureKinectGetCalibrationException.cs b/src/csharp/Record/Exceptions/AzureKinectGetCalibrationException.cs index 48b45ab64..8050101ed 100644 --- a/src/csharp/Record/Exceptions/AzureKinectGetCalibrationException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectGetCalibrationException.cs @@ -1,11 +1,15 @@ -using System; +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +//------------------------------------------------------------------------------ +using System; using System.Collections.Generic; using System.Runtime.Serialization; -using System.Text; namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions { - /// /// Represents errors that occur when getting calibration from a recording. /// diff --git a/src/csharp/Record/Exceptions/AzureKinectGetCaptureException.cs b/src/csharp/Record/Exceptions/AzureKinectGetCaptureException.cs index 7e4ec0b71..8960681fb 100644 --- a/src/csharp/Record/Exceptions/AzureKinectGetCaptureException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectGetCaptureException.cs @@ -1,11 +1,15 @@ -using System; +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +//------------------------------------------------------------------------------ +using System; using System.Collections.Generic; using System.Runtime.Serialization; -using System.Text; namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions { - /// /// Represents errors that occur when getting the next or previous capture. /// diff --git a/src/csharp/Record/Exceptions/AzureKinectGetDataBlockException.cs b/src/csharp/Record/Exceptions/AzureKinectGetDataBlockException.cs index f3685e9c8..e06bfccab 100644 --- a/src/csharp/Record/Exceptions/AzureKinectGetDataBlockException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectGetDataBlockException.cs @@ -1,11 +1,15 @@ -using System; +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +//------------------------------------------------------------------------------ +using System; using System.Collections.Generic; using System.Runtime.Serialization; -using System.Text; namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions { - /// /// Represents errors that occur when getting a data block. /// diff --git a/src/csharp/Record/Exceptions/AzureKinectGetImuSampleException.cs b/src/csharp/Record/Exceptions/AzureKinectGetImuSampleException.cs index d06addf96..089d6e49c 100644 --- a/src/csharp/Record/Exceptions/AzureKinectGetImuSampleException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectGetImuSampleException.cs @@ -1,11 +1,15 @@ -using System; +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +//------------------------------------------------------------------------------ +using System; using System.Collections.Generic; using System.Runtime.Serialization; -using System.Text; namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions { - /// /// Represents errors that occur when reading an IMU sample. /// diff --git a/src/csharp/Record/Exceptions/AzureKinectGetRawCalibrationException.cs b/src/csharp/Record/Exceptions/AzureKinectGetRawCalibrationException.cs index ec7d0aca8..ba9795441 100644 --- a/src/csharp/Record/Exceptions/AzureKinectGetRawCalibrationException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectGetRawCalibrationException.cs @@ -1,11 +1,15 @@ -using System; +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +//------------------------------------------------------------------------------ +using System; using System.Collections.Generic; using System.Runtime.Serialization; -using System.Text; namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions { - /// /// Represents errors that occur when getting raw calibration from a recording. /// diff --git a/src/csharp/Record/Exceptions/AzureKinectGetTagException.cs b/src/csharp/Record/Exceptions/AzureKinectGetTagException.cs index afcaae86b..1aefdbc9d 100644 --- a/src/csharp/Record/Exceptions/AzureKinectGetTagException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectGetTagException.cs @@ -1,13 +1,17 @@ -using System; +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +//------------------------------------------------------------------------------ +using System; using System.Collections.Generic; using System.Runtime.Serialization; -using System.Text; namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions { - /// - /// Represents errors that occur when getting a tag value + /// Represents errors that occur when getting a tag value. /// [Serializable] public class AzureKinectGetTagException : AzureKinectRecordException diff --git a/src/csharp/Record/Exceptions/AzureKinectGetTrackCodecContextException.cs b/src/csharp/Record/Exceptions/AzureKinectGetTrackCodecContextException.cs index 2181246c3..e5f359115 100644 --- a/src/csharp/Record/Exceptions/AzureKinectGetTrackCodecContextException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectGetTrackCodecContextException.cs @@ -1,13 +1,17 @@ -using System; +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +//------------------------------------------------------------------------------ +using System; using System.Collections.Generic; using System.Runtime.Serialization; -using System.Text; namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions { - /// - /// Represents errors that occur when getting a codec context from a track + /// Represents errors that occur when getting a codec context from a track. /// [Serializable] public class AzureKinectGetTrackCodecContextException : AzureKinectRecordException diff --git a/src/csharp/Record/Exceptions/AzureKinectGetTrackNameException.cs b/src/csharp/Record/Exceptions/AzureKinectGetTrackNameException.cs index 977192c51..609089873 100644 --- a/src/csharp/Record/Exceptions/AzureKinectGetTrackNameException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectGetTrackNameException.cs @@ -1,13 +1,17 @@ -using System; +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +//------------------------------------------------------------------------------ +using System; using System.Collections.Generic; using System.Runtime.Serialization; -using System.Text; namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions { - /// - /// Represents errors that occur when getting a track name + /// Represents errors that occur when getting a track name. /// [Serializable] public class AzureKinectGetTrackNameException : AzureKinectRecordException diff --git a/src/csharp/Record/Exceptions/AzureKinectOpenPlaybackException.cs b/src/csharp/Record/Exceptions/AzureKinectOpenPlaybackException.cs index 1725c53de..c098844cc 100644 --- a/src/csharp/Record/Exceptions/AzureKinectOpenPlaybackException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectOpenPlaybackException.cs @@ -1,11 +1,15 @@ -using System; +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +//------------------------------------------------------------------------------ +using System; using System.Collections.Generic; using System.Runtime.Serialization; -using System.Text; namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions { - /// /// Represents errors that occur when opening a recording for playback. /// diff --git a/src/csharp/Record/Exceptions/AzureKinectRecordException.cs b/src/csharp/Record/Exceptions/AzureKinectRecordException.cs index 5ba743eef..5f7404e81 100644 --- a/src/csharp/Record/Exceptions/AzureKinectRecordException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectRecordException.cs @@ -1,17 +1,25 @@ -using System; +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +//------------------------------------------------------------------------------ +using System; using System.Collections.Generic; using System.Runtime.Serialization; -using System.Text; 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. /// - public AzureKinectRecordException() + protected AzureKinectRecordException() { } @@ -20,7 +28,7 @@ public AzureKinectRecordException() /// with a specified error message. /// /// The message that describes the error. - public AzureKinectRecordException(string message) + protected AzureKinectRecordException(string message) : base(message) { } @@ -37,7 +45,7 @@ public AzureKinectRecordException(string message) /// 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 AzureKinectRecordException(string message, Exception innerException) + protected AzureKinectRecordException(string message, Exception innerException) : base(message, innerException) { } @@ -98,7 +106,7 @@ internal static bool IsSuccess(T result) throw new ArgumentException("Result is not of a recognized result type.", nameof(result)); } } - + /// /// Determines if the is a success. /// diff --git a/src/csharp/Record/Exceptions/AzureKinectSeekException.cs b/src/csharp/Record/Exceptions/AzureKinectSeekException.cs index 70700af7d..1c91f5abc 100644 --- a/src/csharp/Record/Exceptions/AzureKinectSeekException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectSeekException.cs @@ -1,13 +1,17 @@ -using System; +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +//------------------------------------------------------------------------------ +using System; using System.Collections.Generic; using System.Runtime.Serialization; -using System.Text; namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions { - /// - /// Represents errors that occur when seeking + /// Represents errors that occur when seeking. /// [Serializable] public class AzureKinectSeekException : AzureKinectRecordException diff --git a/src/csharp/Record/Exceptions/AzureKinectSetColorConversionException.cs b/src/csharp/Record/Exceptions/AzureKinectSetColorConversionException.cs index 6de3cb76e..8f9c6d728 100644 --- a/src/csharp/Record/Exceptions/AzureKinectSetColorConversionException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectSetColorConversionException.cs @@ -1,11 +1,15 @@ -using System; +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +//------------------------------------------------------------------------------ +using System; using System.Collections.Generic; using System.Runtime.Serialization; -using System.Text; namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions { - /// /// Represents errors that occur when setting a color conversion on playback. /// diff --git a/src/csharp/Record/Exceptions/AzureKinectTrackGetVideoSettingsException.cs b/src/csharp/Record/Exceptions/AzureKinectTrackGetVideoSettingsException.cs index 98b157a9e..4e3f10254 100644 --- a/src/csharp/Record/Exceptions/AzureKinectTrackGetVideoSettingsException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectTrackGetVideoSettingsException.cs @@ -1,11 +1,15 @@ -using System; +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +//------------------------------------------------------------------------------ +using System; using System.Collections.Generic; using System.Runtime.Serialization; -using System.Text; namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions { - /// /// Represents errors that occur when an error occurs getting track video settings. /// diff --git a/src/csharp/Record/Exceptions/AzureKinectWriteCaptureException.cs b/src/csharp/Record/Exceptions/AzureKinectWriteCaptureException.cs index 162da069a..a7be4bcab 100644 --- a/src/csharp/Record/Exceptions/AzureKinectWriteCaptureException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectWriteCaptureException.cs @@ -1,13 +1,17 @@ -using System; +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +//------------------------------------------------------------------------------ +using System; using System.Collections.Generic; using System.Runtime.Serialization; -using System.Text; namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions { - /// - /// Represents errors that occur when writing a capture to a recording + /// Represents errors that occur when writing a capture to a recording. /// [Serializable] public class AzureKinectWriteCaptureException : AzureKinectRecordException diff --git a/src/csharp/Record/Exceptions/AzureKinectWriteCustomTrackDataException.cs b/src/csharp/Record/Exceptions/AzureKinectWriteCustomTrackDataException.cs index 4244a4c66..8fece967e 100644 --- a/src/csharp/Record/Exceptions/AzureKinectWriteCustomTrackDataException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectWriteCustomTrackDataException.cs @@ -1,13 +1,17 @@ -using System; +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +//------------------------------------------------------------------------------ +using System; using System.Collections.Generic; using System.Runtime.Serialization; -using System.Text; namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions { - /// - /// Represents errors that occur when writing a custom track + /// Represents errors that occur when writing a custom track. /// [Serializable] public class AzureKinectWriteCustomTrackDataException : AzureKinectRecordException diff --git a/src/csharp/Record/Exceptions/AzureKinectWriteHeaderException.cs b/src/csharp/Record/Exceptions/AzureKinectWriteHeaderException.cs index fcaba6d9b..1d3277753 100644 --- a/src/csharp/Record/Exceptions/AzureKinectWriteHeaderException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectWriteHeaderException.cs @@ -1,13 +1,17 @@ -using System; +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +//------------------------------------------------------------------------------ +using System; using System.Collections.Generic; using System.Runtime.Serialization; -using System.Text; namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions { - /// - /// Represents errors that occur when writing the header + /// Represents errors that occur when writing the header. /// [Serializable] public class AzureKinectWriteHeaderException : AzureKinectRecordException diff --git a/src/csharp/Record/Exceptions/AzureKinectWriteImuSampleException.cs b/src/csharp/Record/Exceptions/AzureKinectWriteImuSampleException.cs index 0278cae03..982843ab2 100644 --- a/src/csharp/Record/Exceptions/AzureKinectWriteImuSampleException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectWriteImuSampleException.cs @@ -1,11 +1,15 @@ -using System; +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +//------------------------------------------------------------------------------ +using System; using System.Collections.Generic; using System.Runtime.Serialization; -using System.Text; namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions { - /// /// Represents errors that occur when writing an IMU sample. /// diff --git a/src/csharp/Record/NativeMethods.cs b/src/csharp/Record/NativeMethods.cs index c1ee0995f..d36a84cdc 100644 --- a/src/csharp/Record/NativeMethods.cs +++ b/src/csharp/Record/NativeMethods.cs @@ -5,8 +5,6 @@ // //------------------------------------------------------------------------------ using System; -using System.Globalization; -using System.Linq.Expressions; using System.Numerics; using System.Runtime.InteropServices; using System.Text; @@ -16,6 +14,7 @@ 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; @@ -58,7 +57,7 @@ public static extern k4a_result_t k4a_record_set_debug_message_handler( [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); @@ -102,7 +101,7 @@ public static extern k4a_result_t k4a_record_set_debug_message_handler( 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, out k4a_record_configuration_t configuration); + public static extern k4a_result_t k4a_playback_get_record_configuration(k4a_playback_t playback_handle, [Out] 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); @@ -123,80 +122,87 @@ public static extern k4a_result_t k4a_record_set_debug_message_handler( 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); + 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)] - 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, 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); + public static extern k4a_result_t k4a_playback_set_color_conversion( + k4a_playback_t playback_handle, + ImageFormat target_format); - [DllImport("k4arecord", CallingConvention = k4aCallingConvention)] - 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, 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); + 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); - + 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); + 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); + 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); + 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); + 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 UInt64 k4a_playback_data_block_get_device_timestamp_usec(k4a_playback_data_block_t data_block_handle); + 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 UInt64 k4a_playback_data_block_get_buffer_size(k4a_playback_data_block_t data_block_handle); + 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, UInt64 offset_usec, PlaybackSeekOrigin origin); + 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 UInt64 k4a_playback_get_recording_length_usec(k4a_playback_t playback_handle); + public static extern ulong k4a_playback_get_recording_length_usec(k4a_playback_t playback_handle); [DllImport("k4arecord", CallingConvention = k4aCallingConvention)] - public static extern UInt64 k4a_playback_get_last_timestamp_usec(k4a_playback_t playback_handle); + 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 { @@ -342,20 +348,23 @@ public ImuSample ToImuSample() [StructLayout(LayoutKind.Sequential)] public class k4a_record_configuration_t { +#pragma warning disable SA1401 // Fields should be private public ImageFormat color_format; public ColorResolution color_resolution; public DepthMode depth_mode; public FPS camera_fps; public bool color_track_enabled; public bool depth_track_enabled; + public bool ir_track_enabled; public bool 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; +#pragma warning restore SA1401 // Fields should be private } - } +#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 index 40204c701..a7a26d077 100644 --- a/src/csharp/Record/Playback.cs +++ b/src/csharp/Record/Playback.cs @@ -1,10 +1,18 @@ -using System; -using System.Collections.Generic; +//------------------------------------------------------------------------------ +// +// 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. @@ -23,51 +31,7 @@ private Playback(NativeMethods.k4a_playback_t handle) } /// - /// Opens an existing recording file for reading. - /// - /// Filesystem path of the existing recording. - /// - 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; - } - } - - /// - /// Get the camera calibration for Azure Kinect device used during recording. The output struct is used as input to all transformation functions. + /// 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. @@ -85,8 +49,7 @@ public Calibration? Calibration if (!this.calibration.HasValue) { - Calibration localCalibration = new Calibration(); - if (NativeMethods.k4a_result_t.K4A_RESULT_SUCCEEDED == NativeMethods.k4a_playback_get_calibration(this.handle, out localCalibration)) + if (NativeMethods.k4a_playback_get_calibration(this.handle, out Calibration localCalibration) == NativeMethods.k4a_result_t.K4A_RESULT_SUCCEEDED) { this.calibration = localCalibration; } @@ -98,7 +61,7 @@ public Calibration? Calibration } /// - /// Get the device configuration used during recording. + /// Gets get the device configuration used during recording. /// public RecordConfiguration RecordConfiguration { @@ -113,7 +76,9 @@ public RecordConfiguration RecordConfiguration if (this.recordConfiguration == null) { - if (NativeMethods.k4a_result_t.K4A_RESULT_SUCCEEDED == NativeMethods.k4a_playback_get_record_configuration(this.handle, out NativeMethods.k4a_record_configuration_t nativeConfig)) + NativeMethods.k4a_record_configuration_t nativeConfig = new NativeMethods.k4a_record_configuration_t(); + + if (NativeMethods.k4a_playback_get_record_configuration(this.handle, nativeConfig) == NativeMethods.k4a_result_t.K4A_RESULT_SUCCEEDED) { this.recordConfiguration = RecordConfiguration.FromNative(nativeConfig); } @@ -125,11 +90,66 @@ public RecordConfiguration RecordConfiguration } /// - /// Checks whether a track with the given track name exists in the playback file. + /// Gets get the number of tracks in a playback file. /// - /// The track name to be checked to see whether it exists or not. - /// - public bool CheckTrackExists(string trackName) + 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) { @@ -138,31 +158,43 @@ public bool CheckTrackExists(string trackName) throw new ObjectDisposedException(nameof(Playback)); } - if (trackName == null) + // 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 ArgumentNullException(nameof(trackName)); + throw new AzureKinectGetRawCalibrationException($"Unexpected result calling {nameof(NativeMethods.k4a_playback_get_raw_calibration)}"); } - return NativeMethods.k4a_playback_check_track_exists(this.handle, trackName); + // 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; } } /// - /// Get the number of tracks in a playback file. + /// Checks whether a track with the given track name exists in the playback file. /// - public int TrackCount + /// 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) { - get + lock (this) { - lock (this) + if (this.disposedValue) { - if (this.disposedValue) - { - throw new ObjectDisposedException(nameof(Playback)); - } + throw new ObjectDisposedException(nameof(Playback)); + } - return checked((int)NativeMethods.k4a_playback_get_track_count(this.handle)); + if (trackName == null) + { + throw new ArgumentNullException(nameof(trackName)); } + + return NativeMethods.k4a_playback_check_track_exists(this.handle, trackName); } } @@ -257,8 +289,8 @@ public RecordVideoSettings GetTrackVideoSettings(string trackName) /// /// Gets the codec id string for a particular track. /// - /// - /// The track name to read the codec id from. + /// 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. @@ -419,7 +451,7 @@ public byte[] GetAttachment(string fileName) } // Allocate a buffer - byte[] buffer= new byte[checked((int)size)]; + byte[] buffer = new byte[checked((int)size)]; // Get the codec id AzureKinectGetTrackNameException.ThrowIfNotSuccess(() => NativeMethods.k4a_playback_get_attachment(this.handle, fileName, buffer, ref size)); @@ -434,14 +466,14 @@ public byte[] GetAttachment(string fileName) /// 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. @@ -477,14 +509,14 @@ public Capture GetNextCapture() /// 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. @@ -520,10 +552,10 @@ public Capture GetPreviousCapture() /// 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. @@ -536,6 +568,7 @@ public ImuSample GetNextImuSample() { 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)) @@ -558,10 +591,10 @@ public ImuSample GetNextImuSample() /// 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. @@ -599,10 +632,10 @@ public ImuSample GetPreviousImuSample() /// /// 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. @@ -643,10 +676,10 @@ public DataBlock GetNextDataBlock(string trackName) /// /// 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. @@ -682,22 +715,22 @@ public DataBlock GetPreviousDataBlock(string trackName) /// /// Seek to a specific timestamp within a recording. /// - /// The timestamp offset to seek to, relative to + /// 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. /// @@ -714,29 +747,6 @@ public void Seek(TimeSpan offset, PlaybackSeekOrigin origin = PlaybackSeekOrigin } } - /// - /// Returns 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); - } - } - /// public void Dispose() { diff --git a/src/csharp/Record/PlaybackSeekOrigin.cs b/src/csharp/Record/PlaybackSeekOrigin.cs index d461cf137..1f8caf5cf 100644 --- a/src/csharp/Record/PlaybackSeekOrigin.cs +++ b/src/csharp/Record/PlaybackSeekOrigin.cs @@ -1,13 +1,29 @@ -using System; -using System.Collections.Generic; -using System.Text; - +//------------------------------------------------------------------------------ +// +// 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, - DeviceTime + + /// + /// The seek operation is specified in the device time. + /// + DeviceTime, } } diff --git a/src/csharp/Record/RecordConfiguration.cs b/src/csharp/Record/RecordConfiguration.cs index 995ad34f0..a58bb02c7 100644 --- a/src/csharp/Record/RecordConfiguration.cs +++ b/src/csharp/Record/RecordConfiguration.cs @@ -1,23 +1,93 @@ -using System; -using System.Collections.Generic; -using System.Text; +//------------------------------------------------------------------------------ +// +// 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; } - public bool IMUTrackEnabled { 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() @@ -28,11 +98,12 @@ internal static RecordConfiguration FromNative(NativeMethods.k4a_record_configur CameraFPS = config.camera_fps, ColorTrackEnabled = config.color_track_enabled, DepthTrackEnabled = config.depth_track_enabled, - IMUTrackEnabled = config.imu_track_enabled, + IRTrackEnabled = config.ir_track_enabled, + ImuTrackEnabled = config.imu_track_enabled, 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) + StartTimestampOffset = TimeSpan.FromTicks(config.start_timestamp_offset_usec), }; } } diff --git a/src/csharp/Record/RecordLogger.cs b/src/csharp/Record/RecordLogger.cs index 5cd50e26b..18eb11ac8 100644 --- a/src/csharp/Record/RecordLogger.cs +++ b/src/csharp/Record/RecordLogger.cs @@ -5,9 +5,7 @@ // //------------------------------------------------------------------------------ using System; -using System.Collections.Generic; using System.Diagnostics; -using System.Text; namespace Microsoft.Azure.Kinect.Sensor.Record { @@ -55,13 +53,7 @@ public static event Action LogMessage /// /// Gets the interface for reading log messages. /// - public static ILoggingProvider LogProvider - { - get - { - return RecordLogger.LoggerProvider; - } - } + public static ILoggingProvider LogProvider => RecordLogger.LoggerProvider; /// /// Initializes the class to begin receiving messages from the Azure Kinect Sensor SDK. diff --git a/src/csharp/Record/RecordSubtitleSettings.cs b/src/csharp/Record/RecordSubtitleSettings.cs index f66c08f57..d7b666b37 100644 --- a/src/csharp/Record/RecordSubtitleSettings.cs +++ b/src/csharp/Record/RecordSubtitleSettings.cs @@ -1,13 +1,26 @@ -using System; -using System.Collections.Generic; +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +//------------------------------------------------------------------------------ +using System; using System.Runtime.InteropServices; -using System.Text; 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 index 0ebb9a02c..bcdd1cc95 100644 --- a/src/csharp/Record/RecordVideoSettings.cs +++ b/src/csharp/Record/RecordVideoSettings.cs @@ -1,15 +1,32 @@ -using System; -using System.Collections.Generic; +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +//------------------------------------------------------------------------------ using System.Runtime.InteropServices; -using System.Text; namespace Microsoft.Azure.Kinect.Sensor.Record { + /// + /// Structure containing additional metadata specific to custom video tracks. + /// [StructLayout(LayoutKind.Sequential)] public class RecordVideoSettings { - public ulong Width { get; set; } - public ulong Height { get; set; } - public ulong FrameRate { get; set; } + /// + /// 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/Record.cs b/src/csharp/Record/Recorder.cs similarity index 92% rename from src/csharp/Record/Record.cs rename to src/csharp/Record/Recorder.cs index 8d2585354..590e68cf7 100644 --- a/src/csharp/Record/Record.cs +++ b/src/csharp/Record/Recorder.cs @@ -1,12 +1,10 @@ //------------------------------------------------------------------------------ -// +// // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. // //------------------------------------------------------------------------------ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.Azure.Kinect.Sensor.Record.Exceptions; namespace Microsoft.Azure.Kinect.Sensor.Record @@ -14,7 +12,7 @@ namespace Microsoft.Azure.Kinect.Sensor.Record /// /// Represents a writable sensor recording. /// - public class Record : IDisposable + public class Recorder : IDisposable { // The native handle for this recording. private readonly NativeMethods.k4a_record_t handle; @@ -22,7 +20,7 @@ public class Record : IDisposable // To detect redundant calls to Dispose private bool disposedValue = false; - private Record(NativeMethods.k4a_record_t handle) + private Recorder(NativeMethods.k4a_record_t handle) { this.handle = handle; } @@ -34,7 +32,7 @@ private Record(NativeMethods.k4a_record_t handle) /// Device to get properties from. May be null for user-generated recordings. /// Parameters used to open the device. /// A new recording object. - public static Record Create(string path, Device device, DeviceConfiguration deviceConfiguration) + public static Recorder Create(string path, Device device, DeviceConfiguration deviceConfiguration) { NativeMethods.k4a_record_t handle = null; if (device != null) @@ -51,7 +49,7 @@ public static Record Create(string path, Device device, DeviceConfiguration devi AzureKinectCreateRecordingException.ThrowIfNotSuccess(path, () => NativeMethods.k4a_record_create(path, IntPtr.Zero, NativeMethods.k4a_device_configuration_t.FromDeviceConfiguration(deviceConfiguration), out handle)); } - return new Record(handle); + return new Recorder(handle); } /// @@ -65,7 +63,7 @@ public void AddTag(string name, string value) { if (this.disposedValue) { - throw new ObjectDisposedException(nameof(Record)); + throw new ObjectDisposedException(nameof(Recorder)); } AzureKinectAddTagException.ThrowIfNotSuccess(() => NativeMethods.k4a_record_add_tag(this.handle, name, value)); @@ -81,7 +79,7 @@ public void AddImuTrack() { if (this.disposedValue) { - throw new ObjectDisposedException(nameof(Record)); + throw new ObjectDisposedException(nameof(Recorder)); } AzureKinectAddImuTrackException.ThrowIfNotSuccess(() => NativeMethods.k4a_record_add_imu_track(this.handle)); @@ -99,7 +97,7 @@ public void AddAttachment(string attachmentName, byte[] buffer) { if (this.disposedValue) { - throw new ObjectDisposedException(nameof(Record)); + throw new ObjectDisposedException(nameof(Recorder)); } AzureKinectAddAttachmentException.ThrowIfNotSuccess(() => NativeMethods.k4a_record_add_attachment(this.handle, attachmentName, buffer, (UIntPtr)buffer.Length)); @@ -110,23 +108,24 @@ public void AddAttachment(string attachmentName, byte[] buffer) /// 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. + /// 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, + public void AddCustomVideoTrack( + string trackName, string codecId, byte[] codecContext, RecordVideoSettings trackSettings) @@ -135,7 +134,7 @@ public void AddCustomVideoTrack(string trackName, { if (this.disposedValue) { - throw new ObjectDisposedException(nameof(Record)); + throw new ObjectDisposedException(nameof(Recorder)); } AzureKinectAddCustomVideoTrackException.ThrowIfNotSuccess(() => NativeMethods.k4a_record_add_custom_video_track( @@ -152,7 +151,7 @@ public void AddCustomVideoTrack(string trackName, /// 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. + /// 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. @@ -160,13 +159,14 @@ public void AddCustomVideoTrack(string trackName, /// /// 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, + public void AddCustomSubtitleTrack( + string trackName, string codecId, byte[] codecContext, RecordSubtitleSettings trackSettings) @@ -175,7 +175,7 @@ public void AddCustomSubtitleTrack(string trackName, { if (this.disposedValue) { - throw new ObjectDisposedException(nameof(Record)); + throw new ObjectDisposedException(nameof(Recorder)); } AzureKinectAddCustomSubtitleTrackException.ThrowIfNotSuccess(() => NativeMethods.k4a_record_add_custom_subtitle_track( @@ -200,7 +200,7 @@ public void WriteHeader() { if (this.disposedValue) { - throw new ObjectDisposedException(nameof(Record)); + throw new ObjectDisposedException(nameof(Recorder)); } AzureKinectWriteHeaderException.ThrowIfNotSuccess(() => NativeMethods.k4a_record_write_header(this.handle)); @@ -213,7 +213,7 @@ public void WriteHeader() /// 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. /// @@ -223,7 +223,7 @@ public void WriteCapture(Capture capture) { if (this.disposedValue) { - throw new ObjectDisposedException(nameof(Record)); + throw new ObjectDisposedException(nameof(Recorder)); } if (capture == null) @@ -238,20 +238,24 @@ public void WriteCapture(Capture capture) } } + /// + /// 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(Record)); + 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, @@ -276,7 +280,8 @@ public void WriteImuSample(ImuSample imuSample) /// 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, + public void WriteCustomTrackData( + string trackName, TimeSpan deviceTimestamp, byte[] customData) { @@ -284,7 +289,7 @@ public void WriteCustomTrackData(string trackName, { if (this.disposedValue) { - throw new ObjectDisposedException(nameof(Record)); + throw new ObjectDisposedException(nameof(Recorder)); } if (trackName == null) @@ -297,7 +302,8 @@ public void WriteCustomTrackData(string trackName, throw new ArgumentNullException(nameof(customData)); } - AzureKinectWriteCustomTrackDataException.ThrowIfNotSuccess(() => NativeMethods.k4a_record_write_custom_track_data(this.handle, + AzureKinectWriteCustomTrackDataException.ThrowIfNotSuccess(() => NativeMethods.k4a_record_write_custom_track_data( + this.handle, trackName, checked((ulong)deviceTimestamp.Ticks / 10), customData, @@ -316,7 +322,7 @@ public void Flush() { if (this.disposedValue) { - throw new ObjectDisposedException(nameof(Record)); + throw new ObjectDisposedException(nameof(Recorder)); } AzureKinectFlushException.ThrowIfNotSuccess(() => NativeMethods.k4a_record_flush(this.handle)); diff --git a/src/csharp/SDK/Device.cs b/src/csharp/SDK/Device.cs index 6d77c6c24..d091dc3d9 100644 --- a/src/csharp/SDK/Device.cs +++ b/src/csharp/SDK/Device.cs @@ -166,6 +166,7 @@ public HardwareVersion Version } } } + /// /// Gets the native handle. /// @@ -214,27 +215,6 @@ public static Device Open(int index = 0) return new Device(handle); } - /// - /// 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; - } - } - /// /// Gets the calibration of the device. /// @@ -320,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); @@ -374,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); @@ -549,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. /// diff --git a/src/csharp/SDK/GlobalSuppressions.cs b/src/csharp/SDK/GlobalSuppressions.cs deleted file mode 100644 index 2f6f2f7b7..000000000 --- a/src/csharp/SDK/GlobalSuppressions.cs +++ /dev/null @@ -1,8 +0,0 @@ - -// This file is used by Code Analysis to maintain SuppressMessage -// attributes that are applied to this project. -// Project-level suppressions either have no target or are given -// a specific target and scoped to a namespace, type, member, etc. - -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Maintainability", "CA1508:Avoid dead conditional code", Justification = "", Scope = "member", Target = "~M:Microsoft.Azure.Kinect.Sensor.Device.GetCapture(System.TimeSpan)~Microsoft.Azure.Kinect.Sensor.Capture")] - diff --git a/src/csharp/SDK/Native/NativeMethods.cs b/src/csharp/SDK/Native/NativeMethods.cs index 1ce4a0bd3..d28acf6c2 100644 --- a/src/csharp/SDK/Native/NativeMethods.cs +++ b/src/csharp/SDK/Native/NativeMethods.cs @@ -372,26 +372,6 @@ public static extern k4a_result_t k4a_set_debug_message_handler( IntPtr message_cb_context, LogLevel min_level); - [DllImport("k4arecord", CallingConvention = k4aCallingConvention, CharSet = CharSet.Ansi)] - [NativeReference] - public static extern k4a_result_t k4a_record_create([MarshalAs(UnmanagedType.LPStr)] string path, k4a_device_t device, k4a_device_configuration_t deviceConfiguration, out k4a_record_t handle); - - [DllImport("k4arecord", CallingConvention = k4aCallingConvention)] - [NativeReference] - public static extern void k4a_record_close(IntPtr handle); - - [DllImport("k4arecord", CallingConvention = k4aCallingConvention, CharSet = CharSet.Ansi)] - [NativeReference] - public static extern k4a_result_t k4a_record_add_tag(k4a_record_t handle, string name, string value); - - [DllImport("k4arecord", CallingConvention = k4aCallingConvention)] - [NativeReference] - public static extern k4a_result_t k4a_record_write_header(k4a_record_t handle); - - [DllImport("k4arecord", CallingConvention = k4aCallingConvention)] - [NativeReference] - public static extern k4a_result_t k4a_record_write_capture(k4a_record_t handle, k4a_capture_t capture); - [NativeReference] [StructLayout(LayoutKind.Sequential)] public struct k4a_version_t @@ -530,20 +510,6 @@ protected override bool ReleaseHandle() } } - 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; - } - } - [StructLayout(LayoutKind.Sequential)] [Native.NativeReference("k4a_imu_sample_t")] public class k4a_imu_sample_t diff --git a/src/csharp/Tests/Record.UnitTests/LoopbackTests.cs b/src/csharp/Tests/Record.UnitTests/LoopbackTests.cs index 2190ef195..623966815 100644 --- a/src/csharp/Tests/Record.UnitTests/LoopbackTests.cs +++ b/src/csharp/Tests/Record.UnitTests/LoopbackTests.cs @@ -51,7 +51,7 @@ public void LoopbackTest1() }; #pragma warning disable CA1508 // Avoid dead conditional code - using (Record record = Record.Create(this.recordingPath, null, deviceConfiguration)) + using (Recorder record = Recorder.Create(this.recordingPath, null, deviceConfiguration)) #pragma warning restore CA1508 // Avoid dead conditional code { record.AddImuTrack(); @@ -187,7 +187,7 @@ public void LoopbackTest1() using (DataBlock videoBlock = playback.GetNextDataBlock("CUSTOM_VIDEO")) { - Assert.AreEqual(customData, videoBlock.Buffer); + Assert.AreEqual(customData, videoBlock.Memory.ToArray()); Assert.AreEqual(TimeSpan.FromSeconds(timeStamp), videoBlock.DeviceTimestamp); } @@ -198,7 +198,7 @@ public void LoopbackTest1() using (DataBlock subtitleBlock = playback.GetNextDataBlock("CUSTOM_SUBTITLE")) { - Assert.AreEqual(customData, subtitleBlock.Buffer); + Assert.AreEqual(customData, subtitleBlock.Memory.ToArray()); Assert.AreEqual(TimeSpan.FromSeconds(timeStamp), subtitleBlock.DeviceTimestamp); } } From 05265bf634b89c94a56c944c2b31817bec471927 Mon Sep 17 00:00:00 2001 From: Brent Allen Date: Wed, 9 Oct 2019 13:16:26 -0700 Subject: [PATCH 14/16] Added more options for native interop --- src/csharp/Record/Playback.cs | 4 +-- src/csharp/SDK/Capture.cs | 10 ++++--- src/csharp/SDK/Image.cs | 40 ++++++++++++++++++++++++++ src/csharp/SDK/Native/NativeMethods.cs | 18 +++++++++++- 4 files changed, 65 insertions(+), 7 deletions(-) diff --git a/src/csharp/Record/Playback.cs b/src/csharp/Record/Playback.cs index a7a26d077..a56fc8f22 100644 --- a/src/csharp/Record/Playback.cs +++ b/src/csharp/Record/Playback.cs @@ -496,7 +496,7 @@ public Capture GetNextCapture() 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(captureHandle); + return new Capture(true, captureHandle); } return null; @@ -539,7 +539,7 @@ public Capture GetPreviousCapture() 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(captureHandle); + return new Capture(true, captureHandle); } return null; diff --git a/src/csharp/SDK/Capture.cs b/src/csharp/SDK/Capture.cs index 7835ec844..ec1b767ce 100644 --- a/src/csharp/SDK/Capture.cs +++ b/src/csharp/SDK/Capture.cs @@ -40,12 +40,14 @@ public Capture() /// /// Initializes a new instance of the class from an existing native handle. /// - /// Native handle to a capture. + /// If false, the constructor will take a new reference on the handle. + /// Native handle to a k4a_capture_t. /// - /// This constructor will take ownership of the existing handle. Disposing this Capture will close the handle. + /// 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(IntPtr handle) - : this(new NativeMethods.k4a_capture_t(handle)) + public Capture(bool takeOwnership, IntPtr handle) + : this(new NativeMethods.k4a_capture_t(takeOwnership, handle)) { } diff --git a/src/csharp/SDK/Image.cs b/src/csharp/SDK/Image.cs index 8ba3e6217..33475010d 100644 --- a/src/csharp/SDK/Image.cs +++ b/src/csharp/SDK/Image.cs @@ -83,6 +83,20 @@ public Image(ImageFormat format, int widthPixels, int heightPixels) #pragma warning restore CA2000 // Dispose objects before losing scope } + /// + /// Initializes a new instance of the class from an existing native handle. + /// + /// If false, the constructor will take a new reference on the handle. + /// Native handle to a k4a_image_t. + /// + /// Disposing this Image will always release a reference on the handle. + /// If is true, the handle may not be owned by any existing Image object. + /// + public Image(bool takeOwnership, IntPtr handle) + : this(new NativeMethods.k4a_image_t(takeOwnership, handle)) + { + } + /// /// Initializes a new instance of the class. /// @@ -468,6 +482,32 @@ public int WhiteBalance } } + /// + /// Gets the native handle. + /// + /// This is the value of the k4a_image_t handle of the native library. + /// + /// This handle value can be used to interoperate with other native libraries that use + /// Azure Kinect objects. + /// + /// When using this handle value, the caller is responsible for ensuring that the + /// Image object does not become disposed. + public IntPtr Handle + { + get + { + lock (this) + { + if (this.disposedValue) + { + throw new ObjectDisposedException(nameof(Image)); + } + + return this.handle.DangerousGetHandle(); + } + } + } + /// /// Gets the pixels of the image. /// diff --git a/src/csharp/SDK/Native/NativeMethods.cs b/src/csharp/SDK/Native/NativeMethods.cs index d28acf6c2..b0c064703 100644 --- a/src/csharp/SDK/Native/NativeMethods.cs +++ b/src/csharp/SDK/Native/NativeMethods.cs @@ -442,9 +442,14 @@ protected override bool ReleaseHandle() public class k4a_capture_t : Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid { - public k4a_capture_t(IntPtr handle) + public k4a_capture_t(bool takeOwnership, IntPtr handle) : base(true) { + if (!takeOwnership) + { + NativeMethods.k4a_image_reference(handle); + } + this.handle = handle; } @@ -472,6 +477,17 @@ protected override bool ReleaseHandle() public class k4a_image_t : Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid { + public k4a_image_t(bool takeOwnership, IntPtr handle) + : base(true) + { + if (!takeOwnership) + { + NativeMethods.k4a_image_reference(handle); + } + + this.handle = handle; + } + private k4a_image_t() : base(true) { From 5999e196969985919f1434d8e6bca9883102fb4b Mon Sep 17 00:00:00 2001 From: Brent Allen Date: Fri, 11 Oct 2019 09:28:40 -0700 Subject: [PATCH 15/16] Added a few tests cases and fixed failures --- src/csharp/K4a.sln | 4 +- .../Exceptions/AzureKinectGetTagException.cs | 16 ++++++++ ...icrosoft.Azure.Kinect.Sensor.Record.csproj | 1 + src/csharp/Record/NativeMethods.cs | 38 +++++++++---------- src/csharp/Record/Playback.cs | 18 ++++++++- src/csharp/Record/RecordConfiguration.cs | 8 ++-- src/csharp/Record/Recorder.cs | 15 +++++++- .../Tests/Record.UnitTests/LoopbackTests.cs | 14 +++++-- src/record/sdk/record.cpp | 5 ++- 9 files changed, 85 insertions(+), 34 deletions(-) diff --git a/src/csharp/K4a.sln b/src/csharp/K4a.sln index 20604e144..5c3f2e0e3 100644 --- a/src/csharp/K4a.sln +++ b/src/csharp/K4a.sln @@ -168,8 +168,8 @@ Global {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|Any CPU - {71EAC57F-4023-4B45-8F9E-4A7C05A6BDB3}.Debug|x64.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 diff --git a/src/csharp/Record/Exceptions/AzureKinectGetTagException.cs b/src/csharp/Record/Exceptions/AzureKinectGetTagException.cs index 1aefdbc9d..7aea3d780 100644 --- a/src/csharp/Record/Exceptions/AzureKinectGetTagException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectGetTagException.cs @@ -97,5 +97,21 @@ internal static void ThrowIfNotSuccess(Func function) } } } + + /// + /// 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/Microsoft.Azure.Kinect.Sensor.Record.csproj b/src/csharp/Record/Microsoft.Azure.Kinect.Sensor.Record.csproj index 2800ddc00..7a462b98f 100644 --- a/src/csharp/Record/Microsoft.Azure.Kinect.Sensor.Record.csproj +++ b/src/csharp/Record/Microsoft.Azure.Kinect.Sensor.Record.csproj @@ -8,6 +8,7 @@ false ..\AzureKinectSensorSDK.ruleset $(BaseOutputPath)\$(AssemblyName)\ + AnyCPU;x64 diff --git a/src/csharp/Record/NativeMethods.cs b/src/csharp/Record/NativeMethods.cs index d36a84cdc..657f47f6d 100644 --- a/src/csharp/Record/NativeMethods.cs +++ b/src/csharp/Record/NativeMethods.cs @@ -101,7 +101,7 @@ public static extern k4a_result_t k4a_record_set_debug_message_handler( 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, [Out] k4a_record_configuration_t configuration); + 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); @@ -277,6 +277,23 @@ public static k4a_device_configuration_t FromDeviceConfiguration(DeviceConfigura } } + [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() @@ -344,25 +361,6 @@ public ImuSample ToImuSample() }; } } - - [StructLayout(LayoutKind.Sequential)] - public class k4a_record_configuration_t - { -#pragma warning disable SA1401 // Fields should be private - public ImageFormat color_format; - public ColorResolution color_resolution; - public DepthMode depth_mode; - public FPS camera_fps; - public bool color_track_enabled; - public bool depth_track_enabled; - public bool ir_track_enabled; - public bool 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; -#pragma warning restore SA1401 // Fields should be private - } } #pragma warning restore CA2101 // Specify marshaling for P/Invoke string arguments #pragma warning restore SA1602 // Enumeration items should be documented diff --git a/src/csharp/Record/Playback.cs b/src/csharp/Record/Playback.cs index a56fc8f22..63dc07dfb 100644 --- a/src/csharp/Record/Playback.cs +++ b/src/csharp/Record/Playback.cs @@ -78,7 +78,7 @@ public RecordConfiguration RecordConfiguration { NativeMethods.k4a_record_configuration_t nativeConfig = new NativeMethods.k4a_record_configuration_t(); - if (NativeMethods.k4a_playback_get_record_configuration(this.handle, nativeConfig) == NativeMethods.k4a_result_t.K4A_RESULT_SUCCEEDED) + if (NativeMethods.k4a_playback_get_record_configuration(this.handle, ref nativeConfig) == NativeMethods.k4a_result_t.K4A_RESULT_SUCCEEDED) { this.recordConfiguration = RecordConfiguration.FromNative(nativeConfig); } @@ -387,7 +387,21 @@ public string GetTag(string name) // Determine the required string size UIntPtr size = new UIntPtr(0); - if (NativeMethods.k4a_playback_get_tag(this.handle, name, null, ref size) != NativeMethods.k4a_buffer_result_t.K4A_BUFFER_RESULT_TOO_SMALL) + NativeMethods.k4a_buffer_result_t result; + +#pragma warning disable CA1508 // Avoid dead conditional code + using (LoggingTracer tracer = new LoggingTracer(LogLevel.Warning, Logger.LogProvider, RecordLogger.LogProvider)) +#pragma warning restore CA1508 // Avoid dead conditional code + { + result = NativeMethods.k4a_playback_get_tag(this.handle, name, null, ref size); + + if (result == NativeMethods.k4a_buffer_result_t.K4A_BUFFER_RESULT_FAILED) + { + AzureKinectGetTagException.ThrowIfNotSuccess(tracer, result); + } + } + + if (result != NativeMethods.k4a_buffer_result_t.K4A_BUFFER_RESULT_TOO_SMALL) { throw new AzureKinectException($"Unexpected internal state calling {nameof(NativeMethods.k4a_playback_get_track_name)}"); } diff --git a/src/csharp/Record/RecordConfiguration.cs b/src/csharp/Record/RecordConfiguration.cs index a58bb02c7..d77283b03 100644 --- a/src/csharp/Record/RecordConfiguration.cs +++ b/src/csharp/Record/RecordConfiguration.cs @@ -96,10 +96,10 @@ internal static RecordConfiguration FromNative(NativeMethods.k4a_record_configur ColorResolution = config.color_resolution, DepthMode = config.depth_mode, CameraFPS = config.camera_fps, - ColorTrackEnabled = config.color_track_enabled, - DepthTrackEnabled = config.depth_track_enabled, - IRTrackEnabled = config.ir_track_enabled, - ImuTrackEnabled = config.imu_track_enabled, + 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), diff --git a/src/csharp/Record/Recorder.cs b/src/csharp/Record/Recorder.cs index 590e68cf7..c5b694272 100644 --- a/src/csharp/Record/Recorder.cs +++ b/src/csharp/Record/Recorder.cs @@ -34,19 +34,30 @@ private Recorder(NativeMethods.k4a_record_t handle) /// 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, NativeMethods.k4a_device_configuration_t.FromDeviceConfiguration(deviceConfiguration), out handle)); + AzureKinectCreateRecordingException.ThrowIfNotSuccess(path, () => NativeMethods.k4a_record_create(path, device.Handle, configuration, out handle)); } } else { - AzureKinectCreateRecordingException.ThrowIfNotSuccess(path, () => NativeMethods.k4a_record_create(path, IntPtr.Zero, NativeMethods.k4a_device_configuration_t.FromDeviceConfiguration(deviceConfiguration), out handle)); + AzureKinectCreateRecordingException.ThrowIfNotSuccess(path, () => NativeMethods.k4a_record_create(path, IntPtr.Zero, configuration, out handle)); } return new Recorder(handle); diff --git a/src/csharp/Tests/Record.UnitTests/LoopbackTests.cs b/src/csharp/Tests/Record.UnitTests/LoopbackTests.cs index 623966815..d80b5b94b 100644 --- a/src/csharp/Tests/Record.UnitTests/LoopbackTests.cs +++ b/src/csharp/Tests/Record.UnitTests/LoopbackTests.cs @@ -50,15 +50,14 @@ public void LoopbackTest1() WiredSyncMode = WiredSyncMode.Subordinate, }; -#pragma warning disable CA1508 // Avoid dead conditional code 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.AddTag("MYTAG1", "one"); + record.AddTag("MYTAG2", "two"); record.WriteHeader(); @@ -130,6 +129,15 @@ public void LoopbackTest1() 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); diff --git a/src/record/sdk/record.cpp b/src/record/sdk/record.cpp index 10b7b771b..aed6685e1 100644 --- a/src/record/sdk/record.cpp +++ b/src/record/sdk/record.cpp @@ -380,7 +380,10 @@ k4a_result_t k4a_record_add_tag(const k4a_record_t recording_handle, const char return K4A_RESULT_FAILED; } - add_tag(context, name, value); + if (NULL == add_tag(context, name, value)) + { + return K4A_RESULT_FAILED; + } return K4A_RESULT_SUCCEEDED; } From 3a3a0594f8db5627faa0594997d3e434cb4135e0 Mon Sep 17 00:00:00 2001 From: Brent Allen Date: Fri, 11 Oct 2019 15:57:55 -0700 Subject: [PATCH 16/16] Update src/csharp/Record/Exceptions/AzureKinectCreateRecordingException.cs Co-Authored-By: Derek M. --- .../Record/Exceptions/AzureKinectCreateRecordingException.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/csharp/Record/Exceptions/AzureKinectCreateRecordingException.cs b/src/csharp/Record/Exceptions/AzureKinectCreateRecordingException.cs index db66af19c..b6ef82e17 100644 --- a/src/csharp/Record/Exceptions/AzureKinectCreateRecordingException.cs +++ b/src/csharp/Record/Exceptions/AzureKinectCreateRecordingException.cs @@ -14,7 +14,7 @@ namespace Microsoft.Azure.Kinect.Sensor.Record.Exceptions /// Represents errors that occur when creating an Azure Kinect sensor recording. /// [Serializable] - public class AzureKinectCreateRecordingException : AzureKinectException + public class AzureKinectCreateRecordingException : AzureKinectRecordException { /// /// Initializes a new instance of the class.