diff --git a/LiveScanClient/KinectClient.vcxproj b/LiveScanClient/KinectClient.vcxproj index cfff532..4bcab63 100644 --- a/LiveScanClient/KinectClient.vcxproj +++ b/LiveScanClient/KinectClient.vcxproj @@ -13,6 +13,7 @@ + @@ -28,6 +29,7 @@ + @@ -91,7 +93,7 @@ true c:\Program Files\Microsoft SDKs\Kinect\v2.0_1409\Lib\x86\;D:\opencv\build\x86\vc12\lib;$(KINECTSDK20_DIR)\lib\x86;$(SolutionDir)lib - opencv_highgui248d.lib;opencv_calib3d248d.lib;opencv_imgproc248d.lib;opencv_core248d.lib;kinect20.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + opencv_highgui248d.lib;opencv_calib3d248d.lib;opencv_imgproc248d.lib;opencv_core248d.lib;kinect20.lib;kernel32.lib;libzstdd.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) NotSet @@ -111,7 +113,7 @@ true true true - opencv_highgui248.lib;opencv_calib3d248.lib;opencv_imgproc248.lib;opencv_core248.lib;kinect20.lib;%(AdditionalDependencies) + opencv_highgui248.lib;opencv_calib3d248.lib;opencv_imgproc248.lib;opencv_core248.lib;kinect20.lib;libzstd.lib;%(AdditionalDependencies) c:\Program Files\Microsoft SDKs\Kinect\v2.0_1409\Lib\x86\;D:\opencv\build\x86\vc12\lib;$(KINECTSDK20_DIR)\lib\x86;$(SolutionDir)lib diff --git a/LiveScanClient/KinectClient.vcxproj.filters b/LiveScanClient/KinectClient.vcxproj.filters index 679951e..a1483aa 100644 --- a/LiveScanClient/KinectClient.vcxproj.filters +++ b/LiveScanClient/KinectClient.vcxproj.filters @@ -54,6 +54,9 @@ Header Files + + Header Files + @@ -86,6 +89,9 @@ Source Files + + Source Files + diff --git a/LiveScanServer/KinectSettings.cs b/LiveScanServer/KinectSettings.cs index 5366760..f5ed1d8 100644 --- a/LiveScanServer/KinectSettings.cs +++ b/LiveScanServer/KinectSettings.cs @@ -35,6 +35,7 @@ namespace KinectServer public bool bStreamOnlyBodies = false; public bool bShowSkeletons = true; + public int iCompressionLevel = 2; // 0 for no compression, 2 is recommended public int nNumICPIterations = 10; public int nNumRefineIters = 2; @@ -96,6 +97,9 @@ namespace KinectServer else lData.Add(0); + bTemp = BitConverter.GetBytes(iCompressionLevel); + lData.AddRange(bTemp); + return lData; } } diff --git a/LiveScanServer/KinectSocket.cs b/LiveScanServer/KinectSocket.cs index b962ba9..39b8d6d 100644 --- a/LiveScanServer/KinectSocket.cs +++ b/LiveScanServer/KinectSocket.cs @@ -42,7 +42,7 @@ namespace KinectServer public List lFrameRGB = new List(); public List lFrameVerts = new List(); - public List lBodies = new List(); + public List lBodies = new List(); public event SocketChangedHandler eChanged; @@ -165,19 +165,16 @@ namespace KinectServer return; } - oSocket.Receive(buffer, 4, SocketFlags.None); - - //string result = System.Text.Encoding.UTF8.GetString(buffer); - //n_to_read = Int32.Parse(result); - + oSocket.Receive(buffer, 8, SocketFlags.None); nToRead = BitConverter.ToInt32(buffer, 0); + int iCompressed = BitConverter.ToInt32(buffer, 4); if (nToRead == -1) { bNoMoreStoredFrames = true; return; } - + buffer = new byte[nToRead]; int nAlreadyRead = 0; @@ -192,6 +189,12 @@ namespace KinectServer nAlreadyRead += oSocket.Receive(buffer, nAlreadyRead, nToRead - nAlreadyRead, SocketFlags.None); } + + + + if (iCompressed == 1) + buffer = ZSTDDecompressor.Decompress(buffer); + //Receive depth and color data int startIdx = 0; diff --git a/LiveScanServer/LiveScanServer.csproj b/LiveScanServer/LiveScanServer.csproj index 18c2276..a86cd5a 100644 --- a/LiveScanServer/LiveScanServer.csproj +++ b/LiveScanServer/LiveScanServer.csproj @@ -32,7 +32,10 @@ 4 - + + False + ..\bin\OpenTK.dll + @@ -68,8 +71,10 @@ + MainWindowForm.cs + Designer ResXFileCodeGenerator diff --git a/LiveScanServer/OpenGLWindow.cs b/LiveScanServer/OpenGLWindow.cs index 0201d04..4063659 100644 --- a/LiveScanServer/OpenGLWindow.cs +++ b/LiveScanServer/OpenGLWindow.cs @@ -18,6 +18,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using System.Globalization; +using System.Windows.Forms.Layout; using System.Diagnostics; @@ -43,6 +44,8 @@ namespace KinectServer static float KEYBOARD_MOVE_SPEED = 0.01f; + bool IsFullscreen = false; + static float MOUSE_ORBIT_SPEED = 0.30f; // 0 = SLOWEST, 1 = FASTEST static float MOUSE_DOLLY_SPEED = 0.2f; // same as above...but much more sensitive static float MOUSE_TRACK_SPEED = 0.003f; // same as above...but much more sensitive @@ -79,7 +82,7 @@ namespace KinectServer /// Creates a 800x600 window with the specified title. public OpenGLWindow() - : base(800, 600) + : base(800, 600, GraphicsMode.Default, "LiveScan") { this.VSync = VSyncMode.Off; MouseUp += new EventHandler(OnMouseButtonUp); @@ -88,7 +91,7 @@ namespace KinectServer MouseWheel += new EventHandler(OnMouseWheelChanged); KeyDown += new EventHandler(OnKeyDown); - + cameraPosition[0] = 0; cameraPosition[1] = 0; cameraPosition[2] = 1.0f; @@ -102,6 +105,22 @@ namespace KinectServer nTickCounter++; } + public void ToggleFullscreen() + { + if (IsFullscreen) + { + WindowBorder = WindowBorder.Resizable; + WindowState = WindowState.Normal; + ClientSize = new System.Drawing.Size(800, 600); + } + else + { + WindowBorder = WindowBorder.Hidden; + WindowState = WindowState.Fullscreen; + } + IsFullscreen = !IsFullscreen; + } + void OnKeyDown(object sender, KeyboardKeyEventArgs e) { // e.Key @@ -289,6 +308,8 @@ namespace KinectServer cameraPosition[2] += KEYBOARD_MOVE_SPEED; if (keyboard[Key.D]) cameraPosition[0] += KEYBOARD_MOVE_SPEED; + if (keyboard[Key.F]) + ToggleFullscreen(); lock (vertices) { diff --git a/LiveScanServer/SettingsForm.Designer.cs b/LiveScanServer/SettingsForm.Designer.cs index ef9c06c..0a4a91b 100644 --- a/LiveScanServer/SettingsForm.Designer.cs +++ b/LiveScanServer/SettingsForm.Designer.cs @@ -34,7 +34,7 @@ this.txtICPIters = new System.Windows.Forms.TextBox(); this.grClient = new System.Windows.Forms.GroupBox(); this.grBody = new System.Windows.Forms.GroupBox(); - this.label1 = new System.Windows.Forms.Label(); + this.chSkeletons = new System.Windows.Forms.CheckBox(); this.chBodyData = new System.Windows.Forms.CheckBox(); this.grMarkers = new System.Windows.Forms.GroupBox(); this.lbX2 = new System.Windows.Forms.Label(); @@ -77,7 +77,8 @@ this.rAsciiPly = new System.Windows.Forms.RadioButton(); this.txtRefinIters = new System.Windows.Forms.TextBox(); this.lbOuterIters = new System.Windows.Forms.Label(); - this.chSkeletons = new System.Windows.Forms.CheckBox(); + this.label2 = new System.Windows.Forms.Label(); + this.cbCompressionLevel = new System.Windows.Forms.ComboBox(); this.grClient.SuspendLayout(); this.grBody.SuspendLayout(); this.grMarkers.SuspendLayout(); @@ -89,18 +90,20 @@ // lbMerge // this.lbMerge.AutoSize = true; - this.lbMerge.Location = new System.Drawing.Point(6, 49); + this.lbMerge.Location = new System.Drawing.Point(7, 69); + this.lbMerge.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lbMerge.Name = "lbMerge"; - this.lbMerge.Size = new System.Drawing.Size(69, 13); + this.lbMerge.Size = new System.Drawing.Size(99, 20); this.lbMerge.TabIndex = 22; this.lbMerge.Text = "Scan saving:"; // // chMerge // this.chMerge.AutoSize = true; - this.chMerge.Location = new System.Drawing.Point(98, 48); + this.chMerge.Location = new System.Drawing.Point(156, 70); + this.chMerge.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.chMerge.Name = "chMerge"; - this.chMerge.Size = new System.Drawing.Size(86, 17); + this.chMerge.Size = new System.Drawing.Size(126, 24); this.chMerge.TabIndex = 23; this.chMerge.Text = "merge scans"; this.chMerge.UseVisualStyleBackColor = true; @@ -109,17 +112,19 @@ // lbICPIters // this.lbICPIters.AutoSize = true; - this.lbICPIters.Location = new System.Drawing.Point(6, 26); + this.lbICPIters.Location = new System.Drawing.Point(7, 34); + this.lbICPIters.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lbICPIters.Name = "lbICPIters"; - this.lbICPIters.Size = new System.Drawing.Size(86, 13); + this.lbICPIters.Size = new System.Drawing.Size(128, 20); this.lbICPIters.TabIndex = 24; this.lbICPIters.Text = "Num of ICP iters:"; // // txtICPIters // - this.txtICPIters.Location = new System.Drawing.Point(98, 23); + this.txtICPIters.Location = new System.Drawing.Point(156, 31); + this.txtICPIters.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtICPIters.Name = "txtICPIters"; - this.txtICPIters.Size = new System.Drawing.Size(38, 20); + this.txtICPIters.Size = new System.Drawing.Size(55, 26); this.txtICPIters.TabIndex = 25; this.txtICPIters.TextChanged += new System.EventHandler(this.txtICPIters_TextChanged); // @@ -129,9 +134,11 @@ this.grClient.Controls.Add(this.grMarkers); this.grClient.Controls.Add(this.grBounding); this.grClient.Controls.Add(this.grFiltering); - this.grClient.Location = new System.Drawing.Point(12, 12); + this.grClient.Location = new System.Drawing.Point(18, 18); + this.grClient.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.grClient.Name = "grClient"; - this.grClient.Size = new System.Drawing.Size(632, 327); + this.grClient.Padding = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.grClient.Size = new System.Drawing.Size(948, 447); this.grClient.TabIndex = 43; this.grClient.TabStop = false; this.grClient.Text = "KinectClient settings"; @@ -139,30 +146,35 @@ // grBody // this.grBody.Controls.Add(this.chSkeletons); - this.grBody.Controls.Add(this.label1); this.grBody.Controls.Add(this.chBodyData); - this.grBody.Location = new System.Drawing.Point(9, 211); + this.grBody.Location = new System.Drawing.Point(14, 325); + this.grBody.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.grBody.Name = "grBody"; - this.grBody.Size = new System.Drawing.Size(249, 110); + this.grBody.Padding = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.grBody.Size = new System.Drawing.Size(374, 101); this.grBody.TabIndex = 47; this.grBody.TabStop = false; this.grBody.Text = "Body data"; // - // label1 + // chSkeletons // - this.label1.AutoSize = true; - this.label1.Location = new System.Drawing.Point(8, 61); - this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(187, 13); - this.label1.TabIndex = 20; - this.label1.Text = "More features will be added here soon"; + this.chSkeletons.AutoSize = true; + this.chSkeletons.Location = new System.Drawing.Point(16, 63); + this.chSkeletons.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.chSkeletons.Name = "chSkeletons"; + this.chSkeletons.Size = new System.Drawing.Size(220, 24); + this.chSkeletons.TabIndex = 21; + this.chSkeletons.Text = "show skeletons in live view"; + this.chSkeletons.UseVisualStyleBackColor = true; + this.chSkeletons.CheckedChanged += new System.EventHandler(this.chSkeletons_CheckedChanged); // // chBodyData // this.chBodyData.AutoSize = true; - this.chBodyData.Location = new System.Drawing.Point(11, 19); + this.chBodyData.Location = new System.Drawing.Point(16, 29); + this.chBodyData.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.chBodyData.Name = "chBodyData"; - this.chBodyData.Size = new System.Drawing.Size(113, 17); + this.chBodyData.Size = new System.Drawing.Size(167, 24); this.chBodyData.TabIndex = 19; this.chBodyData.Text = "stream only bodies"; this.chBodyData.UseVisualStyleBackColor = true; @@ -186,9 +198,11 @@ this.grMarkers.Controls.Add(this.txtTranslationX); this.grMarkers.Controls.Add(this.lisMarkers); this.grMarkers.Controls.Add(this.txtTranslationY); - this.grMarkers.Location = new System.Drawing.Point(271, 19); + this.grMarkers.Location = new System.Drawing.Point(406, 29); + this.grMarkers.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.grMarkers.Name = "grMarkers"; - this.grMarkers.Size = new System.Drawing.Size(353, 185); + this.grMarkers.Padding = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.grMarkers.Size = new System.Drawing.Size(530, 285); this.grMarkers.TabIndex = 45; this.grMarkers.TabStop = false; this.grMarkers.Text = "Calibration markers"; @@ -196,17 +210,19 @@ // lbX2 // this.lbX2.AutoSize = true; - this.lbX2.Location = new System.Drawing.Point(230, 19); + this.lbX2.Location = new System.Drawing.Point(345, 29); + this.lbX2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lbX2.Name = "lbX2"; - this.lbX2.Size = new System.Drawing.Size(14, 13); + this.lbX2.Size = new System.Drawing.Size(20, 20); this.lbX2.TabIndex = 49; this.lbX2.Text = "X"; // // btRemove // - this.btRemove.Location = new System.Drawing.Point(233, 127); + this.btRemove.Location = new System.Drawing.Point(350, 195); + this.btRemove.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.btRemove.Name = "btRemove"; - this.btRemove.Size = new System.Drawing.Size(92, 23); + this.btRemove.Size = new System.Drawing.Size(138, 35); this.btRemove.TabIndex = 59; this.btRemove.Text = "Remove marker"; this.btRemove.UseVisualStyleBackColor = true; @@ -214,68 +230,76 @@ // // txtOrientationZ // - this.txtOrientationZ.Location = new System.Drawing.Point(305, 38); + this.txtOrientationZ.Location = new System.Drawing.Point(458, 58); + this.txtOrientationZ.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtOrientationZ.Name = "txtOrientationZ"; - this.txtOrientationZ.Size = new System.Drawing.Size(38, 20); + this.txtOrientationZ.Size = new System.Drawing.Size(55, 26); this.txtOrientationZ.TabIndex = 48; this.txtOrientationZ.TextChanged += new System.EventHandler(this.txtOrientationZ_TextChanged); // // txtId // - this.txtId.Location = new System.Drawing.Point(217, 93); + this.txtId.Location = new System.Drawing.Point(326, 143); + this.txtId.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtId.Name = "txtId"; - this.txtId.Size = new System.Drawing.Size(38, 20); + this.txtId.Size = new System.Drawing.Size(55, 26); this.txtId.TabIndex = 58; this.txtId.TextChanged += new System.EventHandler(this.txtId_TextChanged); // // lbY2 // this.lbY2.AutoSize = true; - this.lbY2.Location = new System.Drawing.Point(274, 19); + this.lbY2.Location = new System.Drawing.Point(411, 29); + this.lbY2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lbY2.Name = "lbY2"; - this.lbY2.Size = new System.Drawing.Size(14, 13); + this.lbY2.Size = new System.Drawing.Size(20, 20); this.lbY2.TabIndex = 50; this.lbY2.Text = "Y"; // // txtOrientationY // - this.txtOrientationY.Location = new System.Drawing.Point(261, 38); + this.txtOrientationY.Location = new System.Drawing.Point(392, 58); + this.txtOrientationY.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtOrientationY.Name = "txtOrientationY"; - this.txtOrientationY.Size = new System.Drawing.Size(38, 20); + this.txtOrientationY.Size = new System.Drawing.Size(55, 26); this.txtOrientationY.TabIndex = 47; this.txtOrientationY.TextChanged += new System.EventHandler(this.txtOrientationY_TextChanged); // // lbId // this.lbId.AutoSize = true; - this.lbId.Location = new System.Drawing.Point(132, 96); + this.lbId.Location = new System.Drawing.Point(198, 148); + this.lbId.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lbId.Name = "lbId"; - this.lbId.Size = new System.Drawing.Size(54, 13); + this.lbId.Size = new System.Drawing.Size(78, 20); this.lbId.TabIndex = 57; this.lbId.Text = "Marker id:"; // // lbZ2 // this.lbZ2.AutoSize = true; - this.lbZ2.Location = new System.Drawing.Point(317, 19); + this.lbZ2.Location = new System.Drawing.Point(476, 29); + this.lbZ2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lbZ2.Name = "lbZ2"; - this.lbZ2.Size = new System.Drawing.Size(14, 13); + this.lbZ2.Size = new System.Drawing.Size(19, 20); this.lbZ2.TabIndex = 51; this.lbZ2.Text = "Z"; // // txtOrientationX // - this.txtOrientationX.Location = new System.Drawing.Point(217, 38); + this.txtOrientationX.Location = new System.Drawing.Point(326, 58); + this.txtOrientationX.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtOrientationX.Name = "txtOrientationX"; - this.txtOrientationX.Size = new System.Drawing.Size(38, 20); + this.txtOrientationX.Size = new System.Drawing.Size(55, 26); this.txtOrientationX.TabIndex = 46; this.txtOrientationX.TextChanged += new System.EventHandler(this.txtOrientationX_TextChanged); // // btAdd // - this.btAdd.Location = new System.Drawing.Point(135, 127); + this.btAdd.Location = new System.Drawing.Point(202, 195); + this.btAdd.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.btAdd.Name = "btAdd"; - this.btAdd.Size = new System.Drawing.Size(92, 23); + this.btAdd.Size = new System.Drawing.Size(138, 35); this.btAdd.TabIndex = 56; this.btAdd.Text = "Add marker"; this.btAdd.UseVisualStyleBackColor = true; @@ -284,51 +308,58 @@ // lbTranslation // this.lbTranslation.AutoSize = true; - this.lbTranslation.Location = new System.Drawing.Point(132, 68); + this.lbTranslation.Location = new System.Drawing.Point(198, 105); + this.lbTranslation.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lbTranslation.Name = "lbTranslation"; - this.lbTranslation.Size = new System.Drawing.Size(62, 13); + this.lbTranslation.Size = new System.Drawing.Size(91, 20); this.lbTranslation.TabIndex = 52; this.lbTranslation.Text = "Translation:"; // // lbOrientation // this.lbOrientation.AutoSize = true; - this.lbOrientation.Location = new System.Drawing.Point(132, 41); + this.lbOrientation.Location = new System.Drawing.Point(198, 63); + this.lbOrientation.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lbOrientation.Name = "lbOrientation"; - this.lbOrientation.Size = new System.Drawing.Size(61, 13); + this.lbOrientation.Size = new System.Drawing.Size(91, 20); this.lbOrientation.TabIndex = 45; this.lbOrientation.Text = "Orientation:"; // // txtTranslationZ // - this.txtTranslationZ.Location = new System.Drawing.Point(305, 65); + this.txtTranslationZ.Location = new System.Drawing.Point(458, 100); + this.txtTranslationZ.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtTranslationZ.Name = "txtTranslationZ"; - this.txtTranslationZ.Size = new System.Drawing.Size(38, 20); + this.txtTranslationZ.Size = new System.Drawing.Size(55, 26); this.txtTranslationZ.TabIndex = 55; this.txtTranslationZ.TextChanged += new System.EventHandler(this.txtTranslationZ_TextChanged); // // txtTranslationX // - this.txtTranslationX.Location = new System.Drawing.Point(217, 65); + this.txtTranslationX.Location = new System.Drawing.Point(326, 100); + this.txtTranslationX.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtTranslationX.Name = "txtTranslationX"; - this.txtTranslationX.Size = new System.Drawing.Size(38, 20); + this.txtTranslationX.Size = new System.Drawing.Size(55, 26); this.txtTranslationX.TabIndex = 53; this.txtTranslationX.TextChanged += new System.EventHandler(this.txtTranslationX_TextChanged); // // lisMarkers // this.lisMarkers.FormattingEnabled = true; - this.lisMarkers.Location = new System.Drawing.Point(6, 41); + this.lisMarkers.ItemHeight = 20; + this.lisMarkers.Location = new System.Drawing.Point(9, 63); + this.lisMarkers.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.lisMarkers.Name = "lisMarkers"; - this.lisMarkers.Size = new System.Drawing.Size(120, 108); + this.lisMarkers.Size = new System.Drawing.Size(178, 164); this.lisMarkers.TabIndex = 43; this.lisMarkers.SelectedIndexChanged += new System.EventHandler(this.lisMarkers_SelectedIndexChanged); // // txtTranslationY // - this.txtTranslationY.Location = new System.Drawing.Point(261, 65); + this.txtTranslationY.Location = new System.Drawing.Point(392, 100); + this.txtTranslationY.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtTranslationY.Name = "txtTranslationY"; - this.txtTranslationY.Size = new System.Drawing.Size(38, 20); + this.txtTranslationY.Size = new System.Drawing.Size(55, 26); this.txtTranslationY.TabIndex = 54; this.txtTranslationY.TextChanged += new System.EventHandler(this.txtTranslationY_TextChanged); // @@ -345,9 +376,11 @@ this.grBounding.Controls.Add(this.lbZ); this.grBounding.Controls.Add(this.lbY); this.grBounding.Controls.Add(this.lbX); - this.grBounding.Location = new System.Drawing.Point(9, 19); + this.grBounding.Location = new System.Drawing.Point(14, 29); + this.grBounding.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.grBounding.Name = "grBounding"; - this.grBounding.Size = new System.Drawing.Size(249, 91); + this.grBounding.Padding = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.grBounding.Size = new System.Drawing.Size(374, 140); this.grBounding.TabIndex = 46; this.grBounding.TabStop = false; this.grBounding.Text = "Bounding box"; @@ -355,93 +388,104 @@ // lbMin // this.lbMin.AutoSize = true; - this.lbMin.Location = new System.Drawing.Point(8, 38); + this.lbMin.Location = new System.Drawing.Point(12, 58); + this.lbMin.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lbMin.Name = "lbMin"; - this.lbMin.Size = new System.Drawing.Size(65, 13); + this.lbMin.Size = new System.Drawing.Size(95, 20); this.lbMin.TabIndex = 13; this.lbMin.Text = "Min bounds:"; // // txtMaxZ // - this.txtMaxZ.Location = new System.Drawing.Point(181, 62); + this.txtMaxZ.Location = new System.Drawing.Point(272, 95); + this.txtMaxZ.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtMaxZ.Name = "txtMaxZ"; - this.txtMaxZ.Size = new System.Drawing.Size(38, 20); + this.txtMaxZ.Size = new System.Drawing.Size(55, 26); this.txtMaxZ.TabIndex = 23; this.txtMaxZ.TextChanged += new System.EventHandler(this.txtMaxZ_TextChanged); // // txtMaxY // - this.txtMaxY.Location = new System.Drawing.Point(137, 62); + this.txtMaxY.Location = new System.Drawing.Point(206, 95); + this.txtMaxY.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtMaxY.Name = "txtMaxY"; - this.txtMaxY.Size = new System.Drawing.Size(38, 20); + this.txtMaxY.Size = new System.Drawing.Size(55, 26); this.txtMaxY.TabIndex = 22; this.txtMaxY.TextChanged += new System.EventHandler(this.txtMaxY_TextChanged); // // txtMinX // - this.txtMinX.Location = new System.Drawing.Point(93, 35); + this.txtMinX.Location = new System.Drawing.Point(140, 54); + this.txtMinX.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtMinX.Name = "txtMinX"; - this.txtMinX.Size = new System.Drawing.Size(38, 20); + this.txtMinX.Size = new System.Drawing.Size(55, 26); this.txtMinX.TabIndex = 14; this.txtMinX.TextChanged += new System.EventHandler(this.txtMinX_TextChanged); // // txtMaxX // - this.txtMaxX.Location = new System.Drawing.Point(93, 62); + this.txtMaxX.Location = new System.Drawing.Point(140, 95); + this.txtMaxX.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtMaxX.Name = "txtMaxX"; - this.txtMaxX.Size = new System.Drawing.Size(38, 20); + this.txtMaxX.Size = new System.Drawing.Size(55, 26); this.txtMaxX.TabIndex = 21; this.txtMaxX.TextChanged += new System.EventHandler(this.txtMaxX_TextChanged); // // txtMinY // - this.txtMinY.Location = new System.Drawing.Point(137, 35); + this.txtMinY.Location = new System.Drawing.Point(206, 54); + this.txtMinY.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtMinY.Name = "txtMinY"; - this.txtMinY.Size = new System.Drawing.Size(38, 20); + this.txtMinY.Size = new System.Drawing.Size(55, 26); this.txtMinY.TabIndex = 15; this.txtMinY.TextChanged += new System.EventHandler(this.txtMinY_TextChanged); // // lbMax // this.lbMax.AutoSize = true; - this.lbMax.Location = new System.Drawing.Point(8, 65); + this.lbMax.Location = new System.Drawing.Point(12, 100); + this.lbMax.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lbMax.Name = "lbMax"; - this.lbMax.Size = new System.Drawing.Size(68, 13); + this.lbMax.Size = new System.Drawing.Size(99, 20); this.lbMax.TabIndex = 20; this.lbMax.Text = "Max bounds:"; // // txtMinZ // - this.txtMinZ.Location = new System.Drawing.Point(181, 35); + this.txtMinZ.Location = new System.Drawing.Point(272, 54); + this.txtMinZ.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtMinZ.Name = "txtMinZ"; - this.txtMinZ.Size = new System.Drawing.Size(38, 20); + this.txtMinZ.Size = new System.Drawing.Size(55, 26); this.txtMinZ.TabIndex = 16; this.txtMinZ.TextChanged += new System.EventHandler(this.txtMinZ_TextChanged); // // lbZ // this.lbZ.AutoSize = true; - this.lbZ.Location = new System.Drawing.Point(193, 16); + this.lbZ.Location = new System.Drawing.Point(290, 25); + this.lbZ.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lbZ.Name = "lbZ"; - this.lbZ.Size = new System.Drawing.Size(14, 13); + this.lbZ.Size = new System.Drawing.Size(19, 20); this.lbZ.TabIndex = 19; this.lbZ.Text = "Z"; // // lbY // this.lbY.AutoSize = true; - this.lbY.Location = new System.Drawing.Point(150, 16); + this.lbY.Location = new System.Drawing.Point(225, 25); + this.lbY.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lbY.Name = "lbY"; - this.lbY.Size = new System.Drawing.Size(14, 13); + this.lbY.Size = new System.Drawing.Size(20, 20); this.lbY.TabIndex = 18; this.lbY.Text = "Y"; // // lbX // this.lbX.AutoSize = true; - this.lbX.Location = new System.Drawing.Point(106, 16); + this.lbX.Location = new System.Drawing.Point(159, 25); + this.lbX.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lbX.Name = "lbX"; - this.lbX.Size = new System.Drawing.Size(14, 13); + this.lbX.Size = new System.Drawing.Size(20, 20); this.lbX.TabIndex = 17; this.lbX.Text = "X"; // @@ -452,27 +496,31 @@ this.grFiltering.Controls.Add(this.lbFilterNeighbors); this.grFiltering.Controls.Add(this.lbFilterDistance); this.grFiltering.Controls.Add(this.txtFilterDistance); - this.grFiltering.Location = new System.Drawing.Point(9, 116); + this.grFiltering.Location = new System.Drawing.Point(14, 178); + this.grFiltering.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.grFiltering.Name = "grFiltering"; - this.grFiltering.Size = new System.Drawing.Size(249, 88); + this.grFiltering.Padding = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.grFiltering.Size = new System.Drawing.Size(374, 135); this.grFiltering.TabIndex = 45; this.grFiltering.TabStop = false; this.grFiltering.Text = "Filtering"; // // txtFilterNeighbors // - this.txtFilterNeighbors.Location = new System.Drawing.Point(93, 39); + this.txtFilterNeighbors.Location = new System.Drawing.Point(140, 60); + this.txtFilterNeighbors.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtFilterNeighbors.Name = "txtFilterNeighbors"; - this.txtFilterNeighbors.Size = new System.Drawing.Size(38, 20); + this.txtFilterNeighbors.Size = new System.Drawing.Size(55, 26); this.txtFilterNeighbors.TabIndex = 20; this.txtFilterNeighbors.TextChanged += new System.EventHandler(this.txtFilterNeighbors_TextChanged); // // chFilter // this.chFilter.AutoSize = true; - this.chFilter.Location = new System.Drawing.Point(93, 16); + this.chFilter.Location = new System.Drawing.Point(140, 25); + this.chFilter.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.chFilter.Name = "chFilter"; - this.chFilter.Size = new System.Drawing.Size(100, 17); + this.chFilter.Size = new System.Drawing.Size(147, 24); this.chFilter.TabIndex = 18; this.chFilter.Text = "filtering enabled"; this.chFilter.UseVisualStyleBackColor = true; @@ -481,31 +529,36 @@ // lbFilterNeighbors // this.lbFilterNeighbors.AutoSize = true; - this.lbFilterNeighbors.Location = new System.Drawing.Point(8, 42); + this.lbFilterNeighbors.Location = new System.Drawing.Point(12, 65); + this.lbFilterNeighbors.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lbFilterNeighbors.Name = "lbFilterNeighbors"; - this.lbFilterNeighbors.Size = new System.Drawing.Size(67, 13); + this.lbFilterNeighbors.Size = new System.Drawing.Size(98, 20); this.lbFilterNeighbors.TabIndex = 19; this.lbFilterNeighbors.Text = "N neighbors:"; // // lbFilterDistance // this.lbFilterDistance.AutoSize = true; - this.lbFilterDistance.Location = new System.Drawing.Point(8, 68); + this.lbFilterDistance.Location = new System.Drawing.Point(12, 105); + this.lbFilterDistance.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lbFilterDistance.Name = "lbFilterDistance"; - this.lbFilterDistance.Size = new System.Drawing.Size(73, 13); + this.lbFilterDistance.Size = new System.Drawing.Size(106, 20); this.lbFilterDistance.TabIndex = 21; this.lbFilterDistance.Text = "Max distance:"; // // txtFilterDistance // - this.txtFilterDistance.Location = new System.Drawing.Point(93, 65); + this.txtFilterDistance.Location = new System.Drawing.Point(140, 100); + this.txtFilterDistance.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtFilterDistance.Name = "txtFilterDistance"; - this.txtFilterDistance.Size = new System.Drawing.Size(38, 20); + this.txtFilterDistance.Size = new System.Drawing.Size(55, 26); this.txtFilterDistance.TabIndex = 22; this.txtFilterDistance.TextChanged += new System.EventHandler(this.txtFilterDistance_TextChanged); // // grServer // + this.grServer.Controls.Add(this.cbCompressionLevel); + this.grServer.Controls.Add(this.label2); this.grServer.Controls.Add(this.rBinaryPly); this.grServer.Controls.Add(this.lbFormat); this.grServer.Controls.Add(this.rAsciiPly); @@ -515,9 +568,11 @@ this.grServer.Controls.Add(this.txtICPIters); this.grServer.Controls.Add(this.chMerge); this.grServer.Controls.Add(this.lbICPIters); - this.grServer.Location = new System.Drawing.Point(12, 345); + this.grServer.Location = new System.Drawing.Point(18, 478); + this.grServer.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.grServer.Name = "grServer"; - this.grServer.Size = new System.Drawing.Size(632, 111); + this.grServer.Padding = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.grServer.Size = new System.Drawing.Size(948, 175); this.grServer.TabIndex = 44; this.grServer.TabStop = false; this.grServer.Text = "KinectServer settings"; @@ -525,9 +580,10 @@ // rBinaryPly // this.rBinaryPly.AutoSize = true; - this.rBinaryPly.Location = new System.Drawing.Point(179, 71); + this.rBinaryPly.Location = new System.Drawing.Point(277, 105); + this.rBinaryPly.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.rBinaryPly.Name = "rBinaryPly"; - this.rBinaryPly.Size = new System.Drawing.Size(76, 17); + this.rBinaryPly.Size = new System.Drawing.Size(110, 24); this.rBinaryPly.TabIndex = 30; this.rBinaryPly.TabStop = true; this.rBinaryPly.Text = "binary PLY"; @@ -537,18 +593,20 @@ // lbFormat // this.lbFormat.AutoSize = true; - this.lbFormat.Location = new System.Drawing.Point(6, 73); + this.lbFormat.Location = new System.Drawing.Point(7, 106); + this.lbFormat.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lbFormat.Name = "lbFormat"; - this.lbFormat.Size = new System.Drawing.Size(58, 13); + this.lbFormat.Size = new System.Drawing.Size(88, 20); this.lbFormat.TabIndex = 29; this.lbFormat.Text = "File format:"; // // rAsciiPly // this.rAsciiPly.AutoSize = true; - this.rAsciiPly.Location = new System.Drawing.Point(98, 71); + this.rAsciiPly.Location = new System.Drawing.Point(156, 105); + this.rAsciiPly.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.rAsciiPly.Name = "rAsciiPly"; - this.rAsciiPly.Size = new System.Drawing.Size(75, 17); + this.rAsciiPly.Size = new System.Drawing.Size(111, 24); this.rAsciiPly.TabIndex = 28; this.rAsciiPly.TabStop = true; this.rAsciiPly.Text = "ASCII PLY"; @@ -557,40 +615,63 @@ // // txtRefinIters // - this.txtRefinIters.Location = new System.Drawing.Point(266, 23); + this.txtRefinIters.Location = new System.Drawing.Point(408, 31); + this.txtRefinIters.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtRefinIters.Name = "txtRefinIters"; - this.txtRefinIters.Size = new System.Drawing.Size(38, 20); + this.txtRefinIters.Size = new System.Drawing.Size(55, 26); this.txtRefinIters.TabIndex = 27; this.txtRefinIters.TextChanged += new System.EventHandler(this.txtRefinIters_TextChanged); // // lbOuterIters // this.lbOuterIters.AutoSize = true; - this.lbOuterIters.Location = new System.Drawing.Point(142, 26); + this.lbOuterIters.Location = new System.Drawing.Point(222, 36); + this.lbOuterIters.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lbOuterIters.Name = "lbOuterIters"; - this.lbOuterIters.Size = new System.Drawing.Size(118, 13); + this.lbOuterIters.Size = new System.Drawing.Size(178, 20); this.lbOuterIters.TabIndex = 26; this.lbOuterIters.Text = "Num of refinement iters:"; // - // chSkeletons + // label2 // - this.chSkeletons.AutoSize = true; - this.chSkeletons.Location = new System.Drawing.Point(11, 41); - this.chSkeletons.Name = "chSkeletons"; - this.chSkeletons.Size = new System.Drawing.Size(154, 17); - this.chSkeletons.TabIndex = 21; - this.chSkeletons.Text = "show skeletons in live view"; - this.chSkeletons.UseVisualStyleBackColor = true; - this.chSkeletons.CheckedChanged += new System.EventHandler(this.chSkeletons_CheckedChanged); + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(8, 139); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(141, 20); + this.label2.TabIndex = 31; + this.label2.Text = "Compression level:"; + // + // cbCompressionLevel + // + this.cbCompressionLevel.FormattingEnabled = true; + this.cbCompressionLevel.Items.AddRange(new object[] { + "0 (no compression)", + "1", + "2 (recommended)", + "3", + "5", + "7", + "9", + "11", + "13", + "15", + "17", + "19"}); + this.cbCompressionLevel.Location = new System.Drawing.Point(154, 136); + this.cbCompressionLevel.Name = "cbCompressionLevel"; + this.cbCompressionLevel.Size = new System.Drawing.Size(169, 28); + this.cbCompressionLevel.TabIndex = 32; + this.cbCompressionLevel.SelectedIndexChanged += new System.EventHandler(this.cbCompressionLevel_SelectedIndexChanged); // // SettingsForm // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(656, 468); + this.ClientSize = new System.Drawing.Size(984, 663); this.Controls.Add(this.grServer); this.Controls.Add(this.grClient); this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.MaximizeBox = false; this.Name = "SettingsForm"; this.Text = "Settings"; @@ -657,11 +738,12 @@ private System.Windows.Forms.Label lbX; private System.Windows.Forms.GroupBox grBody; private System.Windows.Forms.CheckBox chBodyData; - private System.Windows.Forms.Label label1; private System.Windows.Forms.RadioButton rBinaryPly; private System.Windows.Forms.Label lbFormat; private System.Windows.Forms.RadioButton rAsciiPly; private System.Windows.Forms.CheckBox chSkeletons; + private System.Windows.Forms.ComboBox cbCompressionLevel; + private System.Windows.Forms.Label label2; } } \ No newline at end of file diff --git a/LiveScanServer/SettingsForm.cs b/LiveScanServer/SettingsForm.cs index 0ec42c6..9fdae90 100644 --- a/LiveScanServer/SettingsForm.cs +++ b/LiveScanServer/SettingsForm.cs @@ -54,6 +54,8 @@ namespace KinectServer chBodyData.Checked = oSettings.bStreamOnlyBodies; chSkeletons.Checked = oSettings.bShowSkeletons; + cbCompressionLevel.SelectedText = oSettings.iCompressionLevel.ToString(); + chMerge.Checked = oSettings.bMergeScansForSave; txtICPIters.Text = oSettings.nNumICPIterations.ToString(); txtRefinIters.Text = oSettings.nNumRefineIters.ToString(); @@ -316,5 +318,22 @@ namespace KinectServer { oSettings.bShowSkeletons = chSkeletons.Checked; } + + private void cbCompressionLevel_SelectedIndexChanged(object sender, EventArgs e) + { + int index = cbCompressionLevel.SelectedIndex; + if (index == 0) + oSettings.iCompressionLevel = 0; + else if (index == 2) + oSettings.iCompressionLevel = 2; + else + { + string value = cbCompressionLevel.SelectedItem.ToString(); + bool tryParse = Int32.TryParse(value, out oSettings.iCompressionLevel); + if (!tryParse) + oSettings.iCompressionLevel = 0; + } + UpdateClients(); + } } } diff --git a/LiveScanServer/ZSTDDecompressor.cs b/LiveScanServer/ZSTDDecompressor.cs new file mode 100644 index 0000000..7e3af98 --- /dev/null +++ b/LiveScanServer/ZSTDDecompressor.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Runtime.InteropServices; +using size_t = System.UIntPtr; + +namespace KinectServer +{ + static class ZSTDDecompressor + { + private const string dllName = "libzstd.dll"; + + [DllImport(dllName, CallingConvention = CallingConvention.Cdecl)] + public static extern ulong ZSTD_getDecompressedSize(IntPtr src, size_t srcSize); + + [DllImport(dllName, CallingConvention = CallingConvention.Cdecl)] + public static extern size_t ZSTD_decompress(IntPtr dst, size_t dstSize, + IntPtr src, size_t srcSize); + + public static byte[] Decompress(byte []array) + { + int size = array.Count(); + IntPtr ptr = Marshal.AllocHGlobal(size); + Marshal.Copy(array, 0, ptr, size); + + int outSize = (int)ZSTD_getDecompressedSize(ptr, (size_t)size); + + IntPtr outPtr = Marshal.AllocHGlobal(outSize); + ZSTD_decompress(outPtr, (size_t)outSize, ptr, (size_t)size); + + byte[] outArray = new byte[outSize]; + Marshal.Copy(outPtr, outArray, 0, outSize); + + Marshal.FreeHGlobal(ptr); + Marshal.FreeHGlobal(outPtr); + return outArray; + } + } +} diff --git a/bin/LiveScanClient.exe b/bin/LiveScanClient.exe new file mode 100644 index 0000000..1e51318 Binary files /dev/null and b/bin/LiveScanClient.exe differ diff --git a/bin/LiveScanServer.exe b/bin/LiveScanServer.exe new file mode 100644 index 0000000..e7800dc Binary files /dev/null and b/bin/LiveScanServer.exe differ diff --git a/bin/libzstd.dll b/bin/libzstd.dll new file mode 100644 index 0000000..94f9dc1 Binary files /dev/null and b/bin/libzstd.dll differ diff --git a/include/LiveScanClient/frameFileWriterReader.h b/include/LiveScanClient/frameFileWriterReader.h new file mode 100644 index 0000000..855d2ce --- /dev/null +++ b/include/LiveScanClient/frameFileWriterReader.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include +#include +#include +#include "utils.h" + +class FrameFileWriterReader +{ +public: + FrameFileWriterReader(); + void openNewFileForWriting(); + void openCurrentFileForReading(); + + // leave filename blank if you want the filename to be generated from the date + void setCurrentFilename(std::string filename = ""); + + void writeFrame(std::vector points, std::vector colors); + bool readFrame(std::vector &outPoints, std::vector &outColors); + + bool openedForWriting() { return m_bFileOpenedForWriting; } + bool openedForReading() { return m_bFileOpenedForReading; } + + + void closeFileIfOpened(); + + ~FrameFileWriterReader(); + +private: + void resetTimer(); + int getRecordingTimeMilliseconds(); + + FILE *m_pFileHandle = nullptr; + bool m_bFileOpenedForWriting = false; + bool m_bFileOpenedForReading = false; + + std::string m_sFilename = ""; + + std::chrono::steady_clock::time_point recording_start_time; +}; \ No newline at end of file diff --git a/include/LiveScanClient/liveScanClient.h b/include/LiveScanClient/liveScanClient.h index 4f4628d..51684b5 100644 --- a/include/LiveScanClient/liveScanClient.h +++ b/include/LiveScanClient/liveScanClient.h @@ -20,6 +20,7 @@ #include "calibration.h" #include "utils.h" #include "KinectCapture.h" +#include "frameFileWriterReader.h" #include #include @@ -52,7 +53,13 @@ private: bool m_bConfirmCaptured; bool m_bConfirmCalibrated; bool m_bShowDepth; - + bool m_bFrameCompression; + int m_iCompressionLevel; + bool m_bCaptureToFile; // false if capture frames to memory + + FrameFileWriterReader m_framesFileWriterReader; + + SocketClient *m_pClientSocket; std::vector m_vBounds; diff --git a/include/zstd.h b/include/zstd.h new file mode 100644 index 0000000..333feff --- /dev/null +++ b/include/zstd.h @@ -0,0 +1,692 @@ +/* + * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#if defined (__cplusplus) +extern "C" { +#endif + +#ifndef ZSTD_H_235446 +#define ZSTD_H_235446 + +/* ====== Dependency ======*/ +#include /* size_t */ + + +/* ===== ZSTDLIB_API : control library symbols visibility ===== */ +#if defined(__GNUC__) && (__GNUC__ >= 4) +# define ZSTDLIB_VISIBILITY __attribute__ ((visibility ("default"))) +#else +# define ZSTDLIB_VISIBILITY +#endif +#if defined(ZSTD_DLL_EXPORT) && (ZSTD_DLL_EXPORT==1) +# define ZSTDLIB_API __declspec(dllexport) ZSTDLIB_VISIBILITY +#elif defined(ZSTD_DLL_IMPORT) && (ZSTD_DLL_IMPORT==1) +# define ZSTDLIB_API __declspec(dllimport) ZSTDLIB_VISIBILITY /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/ +#else +# define ZSTDLIB_API ZSTDLIB_VISIBILITY +#endif + + +/******************************************************************************************************* + Introduction + + zstd, short for Zstandard, is a fast lossless compression algorithm, targeting real-time compression scenarios + at zlib-level and better compression ratios. The zstd compression library provides in-memory compression and + decompression functions. The library supports compression levels from 1 up to ZSTD_maxCLevel() which is 22. + Levels >= 20, labelled `--ultra`, should be used with caution, as they require more memory. + Compression can be done in: + - a single step (described as Simple API) + - a single step, reusing a context (described as Explicit memory management) + - unbounded multiple steps (described as Streaming compression) + The compression ratio achievable on small data can be highly improved using compression with a dictionary in: + - a single step (described as Simple dictionary API) + - a single step, reusing a dictionary (described as Fast dictionary API) + + Advanced experimental functions can be accessed using #define ZSTD_STATIC_LINKING_ONLY before including zstd.h. + These APIs shall never be used with a dynamic library. + They are not "stable", their definition may change in the future. Only static linking is allowed. +*********************************************************************************************************/ + +/*------ Version ------*/ +#define ZSTD_VERSION_MAJOR 1 +#define ZSTD_VERSION_MINOR 1 +#define ZSTD_VERSION_RELEASE 3 + +#define ZSTD_LIB_VERSION ZSTD_VERSION_MAJOR.ZSTD_VERSION_MINOR.ZSTD_VERSION_RELEASE +#define ZSTD_QUOTE(str) #str +#define ZSTD_EXPAND_AND_QUOTE(str) ZSTD_QUOTE(str) +#define ZSTD_VERSION_STRING ZSTD_EXPAND_AND_QUOTE(ZSTD_LIB_VERSION) + +#define ZSTD_VERSION_NUMBER (ZSTD_VERSION_MAJOR *100*100 + ZSTD_VERSION_MINOR *100 + ZSTD_VERSION_RELEASE) +ZSTDLIB_API unsigned ZSTD_versionNumber(void); /**< library version number; to be used when checking dll version */ + + +/*************************************** +* Simple API +***************************************/ +/*! ZSTD_compress() : + Compresses `src` content as a single zstd compressed frame into already allocated `dst`. + Hint : compression runs faster if `dstCapacity` >= `ZSTD_compressBound(srcSize)`. + @return : compressed size written into `dst` (<= `dstCapacity), + or an error code if it fails (which can be tested using ZSTD_isError()). */ +ZSTDLIB_API size_t ZSTD_compress( void* dst, size_t dstCapacity, + const void* src, size_t srcSize, + int compressionLevel); + +/*! ZSTD_decompress() : + `compressedSize` : must be the _exact_ size of a single compressed frame. + `dstCapacity` is an upper bound of originalSize. + If user cannot imply a maximum upper bound, it's better to use streaming mode to decompress data. + @return : the number of bytes decompressed into `dst` (<= `dstCapacity`), + or an errorCode if it fails (which can be tested using ZSTD_isError()). */ +ZSTDLIB_API size_t ZSTD_decompress( void* dst, size_t dstCapacity, + const void* src, size_t compressedSize); + +/*! ZSTD_getDecompressedSize() : +* 'src' is the start of a zstd compressed frame. +* @return : content size to be decompressed, as a 64-bits value _if known_, 0 otherwise. +* note 1 : decompressed size is an optional field, that may not be present, especially in streaming mode. +* When `return==0`, data to decompress could be any size. +* In which case, it's necessary to use streaming mode to decompress data. +* Optionally, application can still use ZSTD_decompress() while relying on implied limits. +* (For example, data may be necessarily cut into blocks <= 16 KB). +* note 2 : decompressed size is always present when compression is done with ZSTD_compress() +* note 3 : decompressed size can be very large (64-bits value), +* potentially larger than what local system can handle as a single memory segment. +* In which case, it's necessary to use streaming mode to decompress data. +* note 4 : If source is untrusted, decompressed size could be wrong or intentionally modified. +* Always ensure result fits within application's authorized limits. +* Each application can set its own limits. +* note 5 : when `return==0`, if precise failure cause is needed, use ZSTD_getFrameParams() to know more. */ +ZSTDLIB_API unsigned long long ZSTD_getDecompressedSize(const void* src, size_t srcSize); + + +/*====== Helper functions ======*/ +ZSTDLIB_API int ZSTD_maxCLevel(void); /*!< maximum compression level available */ +ZSTDLIB_API size_t ZSTD_compressBound(size_t srcSize); /*!< maximum compressed size in worst case scenario */ +ZSTDLIB_API unsigned ZSTD_isError(size_t code); /*!< tells if a `size_t` function result is an error code */ +ZSTDLIB_API const char* ZSTD_getErrorName(size_t code); /*!< provides readable string from an error code */ + + +/*************************************** +* Explicit memory management +***************************************/ +/*= Compression context +* When compressing many times, +* it is recommended to allocate a context just once, and re-use it for each successive compression operation. +* This will make workload friendlier for system's memory. +* Use one context per thread for parallel execution in multi-threaded environments. */ +typedef struct ZSTD_CCtx_s ZSTD_CCtx; +ZSTDLIB_API ZSTD_CCtx* ZSTD_createCCtx(void); +ZSTDLIB_API size_t ZSTD_freeCCtx(ZSTD_CCtx* cctx); + +/*! ZSTD_compressCCtx() : + Same as ZSTD_compress(), requires an allocated ZSTD_CCtx (see ZSTD_createCCtx()). */ +ZSTDLIB_API size_t ZSTD_compressCCtx(ZSTD_CCtx* ctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, int compressionLevel); + +/*= Decompression context */ +typedef struct ZSTD_DCtx_s ZSTD_DCtx; +ZSTDLIB_API ZSTD_DCtx* ZSTD_createDCtx(void); +ZSTDLIB_API size_t ZSTD_freeDCtx(ZSTD_DCtx* dctx); + +/*! ZSTD_decompressDCtx() : +* Same as ZSTD_decompress(), requires an allocated ZSTD_DCtx (see ZSTD_createDCtx()). */ +ZSTDLIB_API size_t ZSTD_decompressDCtx(ZSTD_DCtx* ctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); + + +/************************** +* Simple dictionary API +***************************/ +/*! ZSTD_compress_usingDict() : +* Compression using a predefined Dictionary (see dictBuilder/zdict.h). +* Note : This function loads the dictionary, resulting in significant startup delay. +* Note : When `dict == NULL || dictSize < 8` no dictionary is used. */ +ZSTDLIB_API size_t ZSTD_compress_usingDict(ZSTD_CCtx* ctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize, + const void* dict,size_t dictSize, + int compressionLevel); + +/*! ZSTD_decompress_usingDict() : +* Decompression using a predefined Dictionary (see dictBuilder/zdict.h). +* Dictionary must be identical to the one used during compression. +* Note : This function loads the dictionary, resulting in significant startup delay. +* Note : When `dict == NULL || dictSize < 8` no dictionary is used. */ +ZSTDLIB_API size_t ZSTD_decompress_usingDict(ZSTD_DCtx* dctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize, + const void* dict,size_t dictSize); + + +/**************************** +* Fast dictionary API +****************************/ +typedef struct ZSTD_CDict_s ZSTD_CDict; + +/*! ZSTD_createCDict() : +* When compressing multiple messages / blocks with the same dictionary, it's recommended to load it just once. +* ZSTD_createCDict() will create a digested dictionary, ready to start future compression operations without startup delay. +* ZSTD_CDict can be created once and used by multiple threads concurrently, as its usage is read-only. +* `dictBuffer` can be released after ZSTD_CDict creation, as its content is copied within CDict */ +ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict(const void* dictBuffer, size_t dictSize, int compressionLevel); + +/*! ZSTD_freeCDict() : +* Function frees memory allocated by ZSTD_createCDict(). */ +ZSTDLIB_API size_t ZSTD_freeCDict(ZSTD_CDict* CDict); + +/*! ZSTD_compress_usingCDict() : +* Compression using a digested Dictionary. +* Faster startup than ZSTD_compress_usingDict(), recommended when same dictionary is used multiple times. +* Note that compression level is decided during dictionary creation. */ +ZSTDLIB_API size_t ZSTD_compress_usingCDict(ZSTD_CCtx* cctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize, + const ZSTD_CDict* cdict); + + +typedef struct ZSTD_DDict_s ZSTD_DDict; + +/*! ZSTD_createDDict() : +* Create a digested dictionary, ready to start decompression operation without startup delay. +* dictBuffer can be released after DDict creation, as its content is copied inside DDict */ +ZSTDLIB_API ZSTD_DDict* ZSTD_createDDict(const void* dictBuffer, size_t dictSize); + +/*! ZSTD_freeDDict() : +* Function frees memory allocated with ZSTD_createDDict() */ +ZSTDLIB_API size_t ZSTD_freeDDict(ZSTD_DDict* ddict); + +/*! ZSTD_decompress_usingDDict() : +* Decompression using a digested Dictionary. +* Faster startup than ZSTD_decompress_usingDict(), recommended when same dictionary is used multiple times. */ +ZSTDLIB_API size_t ZSTD_decompress_usingDDict(ZSTD_DCtx* dctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize, + const ZSTD_DDict* ddict); + + +/**************************** +* Streaming +****************************/ + +typedef struct ZSTD_inBuffer_s { + const void* src; /**< start of input buffer */ + size_t size; /**< size of input buffer */ + size_t pos; /**< position where reading stopped. Will be updated. Necessarily 0 <= pos <= size */ +} ZSTD_inBuffer; + +typedef struct ZSTD_outBuffer_s { + void* dst; /**< start of output buffer */ + size_t size; /**< size of output buffer */ + size_t pos; /**< position where writing stopped. Will be updated. Necessarily 0 <= pos <= size */ +} ZSTD_outBuffer; + + + +/*-*********************************************************************** +* Streaming compression - HowTo +* +* A ZSTD_CStream object is required to track streaming operation. +* Use ZSTD_createCStream() and ZSTD_freeCStream() to create/release resources. +* ZSTD_CStream objects can be reused multiple times on consecutive compression operations. +* It is recommended to re-use ZSTD_CStream in situations where many streaming operations will be achieved consecutively, +* since it will play nicer with system's memory, by re-using already allocated memory. +* Use one separate ZSTD_CStream per thread for parallel execution. +* +* Start a new compression by initializing ZSTD_CStream. +* Use ZSTD_initCStream() to start a new compression operation. +* Use ZSTD_initCStream_usingDict() or ZSTD_initCStream_usingCDict() for a compression which requires a dictionary (experimental section) +* +* Use ZSTD_compressStream() repetitively to consume input stream. +* The function will automatically update both `pos` fields. +* Note that it may not consume the entire input, in which case `pos < size`, +* and it's up to the caller to present again remaining data. +* @return : a size hint, preferred nb of bytes to use as input for next function call +* or an error code, which can be tested using ZSTD_isError(). +* Note 1 : it's just a hint, to help latency a little, any other value will work fine. +* Note 2 : size hint is guaranteed to be <= ZSTD_CStreamInSize() +* +* At any moment, it's possible to flush whatever data remains within internal buffer, using ZSTD_flushStream(). +* `output->pos` will be updated. +* Note that some content might still be left within internal buffer if `output->size` is too small. +* @return : nb of bytes still present within internal buffer (0 if it's empty) +* or an error code, which can be tested using ZSTD_isError(). +* +* ZSTD_endStream() instructs to finish a frame. +* It will perform a flush and write frame epilogue. +* The epilogue is required for decoders to consider a frame completed. +* Similar to ZSTD_flushStream(), it may not be able to flush the full content if `output->size` is too small. +* In which case, call again ZSTD_endStream() to complete the flush. +* @return : nb of bytes still present within internal buffer (0 if it's empty, hence compression completed) +* or an error code, which can be tested using ZSTD_isError(). +* +* *******************************************************************/ + +typedef struct ZSTD_CStream_s ZSTD_CStream; +ZSTDLIB_API ZSTD_CStream* ZSTD_createCStream(void); +ZSTDLIB_API size_t ZSTD_freeCStream(ZSTD_CStream* zcs); + +ZSTDLIB_API size_t ZSTD_initCStream(ZSTD_CStream* zcs, int compressionLevel); +ZSTDLIB_API size_t ZSTD_compressStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output, ZSTD_inBuffer* input); +ZSTDLIB_API size_t ZSTD_flushStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output); +ZSTDLIB_API size_t ZSTD_endStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output); + +ZSTDLIB_API size_t ZSTD_CStreamInSize(void); /**< recommended size for input buffer */ +ZSTDLIB_API size_t ZSTD_CStreamOutSize(void); /**< recommended size for output buffer. Guarantee to successfully flush at least one complete compressed block in all circumstances. */ + + + +/*-*************************************************************************** +* Streaming decompression - HowTo +* +* A ZSTD_DStream object is required to track streaming operations. +* Use ZSTD_createDStream() and ZSTD_freeDStream() to create/release resources. +* ZSTD_DStream objects can be re-used multiple times. +* +* Use ZSTD_initDStream() to start a new decompression operation, +* or ZSTD_initDStream_usingDict() if decompression requires a dictionary. +* @return : recommended first input size +* +* Use ZSTD_decompressStream() repetitively to consume your input. +* The function will update both `pos` fields. +* If `input.pos < input.size`, some input has not been consumed. +* It's up to the caller to present again remaining data. +* If `output.pos < output.size`, decoder has flushed everything it could. +* @return : 0 when a frame is completely decoded and fully flushed, +* an error code, which can be tested using ZSTD_isError(), +* any other value > 0, which means there is still some decoding to do to complete current frame. +* The return value is a suggested next input size (a hint to improve latency) that will never load more than the current frame. +* *******************************************************************************/ + +typedef struct ZSTD_DStream_s ZSTD_DStream; +ZSTDLIB_API ZSTD_DStream* ZSTD_createDStream(void); +ZSTDLIB_API size_t ZSTD_freeDStream(ZSTD_DStream* zds); + +ZSTDLIB_API size_t ZSTD_initDStream(ZSTD_DStream* zds); +ZSTDLIB_API size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inBuffer* input); + +ZSTDLIB_API size_t ZSTD_DStreamInSize(void); /*!< recommended size for input buffer */ +ZSTDLIB_API size_t ZSTD_DStreamOutSize(void); /*!< recommended size for output buffer. Guarantee to successfully flush at least one complete block in all circumstances. */ + +#endif /* ZSTD_H_235446 */ + + +#if defined(ZSTD_STATIC_LINKING_ONLY) && !defined(ZSTD_H_ZSTD_STATIC_LINKING_ONLY) +#define ZSTD_H_ZSTD_STATIC_LINKING_ONLY + +/**************************************************************************************** + * START OF ADVANCED AND EXPERIMENTAL FUNCTIONS + * The definitions in this section are considered experimental. + * They should never be used with a dynamic library, as they may change in the future. + * They are provided for advanced usages. + * Use them only in association with static linking. + * ***************************************************************************************/ + +/* --- Constants ---*/ +#define ZSTD_MAGICNUMBER 0xFD2FB528 /* >= v0.8.0 */ +#define ZSTD_MAGIC_SKIPPABLE_START 0x184D2A50U + +#define ZSTD_WINDOWLOG_MAX_32 25 +#define ZSTD_WINDOWLOG_MAX_64 27 +#define ZSTD_WINDOWLOG_MAX ((U32)(MEM_32bits() ? ZSTD_WINDOWLOG_MAX_32 : ZSTD_WINDOWLOG_MAX_64)) +#define ZSTD_WINDOWLOG_MIN 10 +#define ZSTD_HASHLOG_MAX ZSTD_WINDOWLOG_MAX +#define ZSTD_HASHLOG_MIN 6 +#define ZSTD_CHAINLOG_MAX (ZSTD_WINDOWLOG_MAX+1) +#define ZSTD_CHAINLOG_MIN ZSTD_HASHLOG_MIN +#define ZSTD_HASHLOG3_MAX 17 +#define ZSTD_SEARCHLOG_MAX (ZSTD_WINDOWLOG_MAX-1) +#define ZSTD_SEARCHLOG_MIN 1 +#define ZSTD_SEARCHLENGTH_MAX 7 /* only for ZSTD_fast, other strategies are limited to 6 */ +#define ZSTD_SEARCHLENGTH_MIN 3 /* only for ZSTD_btopt, other strategies are limited to 4 */ +#define ZSTD_TARGETLENGTH_MIN 4 +#define ZSTD_TARGETLENGTH_MAX 999 + +#define ZSTD_FRAMEHEADERSIZE_MAX 18 /* for static allocation */ +#define ZSTD_FRAMEHEADERSIZE_MIN 6 +static const size_t ZSTD_frameHeaderSize_prefix = 5; +static const size_t ZSTD_frameHeaderSize_min = ZSTD_FRAMEHEADERSIZE_MIN; +static const size_t ZSTD_frameHeaderSize_max = ZSTD_FRAMEHEADERSIZE_MAX; +static const size_t ZSTD_skippableHeaderSize = 8; /* magic number + skippable frame length */ + + +/*--- Advanced types ---*/ +typedef enum { ZSTD_fast, ZSTD_dfast, ZSTD_greedy, ZSTD_lazy, ZSTD_lazy2, ZSTD_btlazy2, ZSTD_btopt, ZSTD_btopt2 } ZSTD_strategy; /* from faster to stronger */ + +typedef struct { + unsigned windowLog; /**< largest match distance : larger == more compression, more memory needed during decompression */ + unsigned chainLog; /**< fully searched segment : larger == more compression, slower, more memory (useless for fast) */ + unsigned hashLog; /**< dispatch table : larger == faster, more memory */ + unsigned searchLog; /**< nb of searches : larger == more compression, slower */ + unsigned searchLength; /**< match length searched : larger == faster decompression, sometimes less compression */ + unsigned targetLength; /**< acceptable match size for optimal parser (only) : larger == more compression, slower */ + ZSTD_strategy strategy; +} ZSTD_compressionParameters; + +typedef struct { + unsigned contentSizeFlag; /**< 1: content size will be in frame header (if known). */ + unsigned checksumFlag; /**< 1: will generate a 22-bits checksum at end of frame, to be used for error detection by decompressor */ + unsigned noDictIDFlag; /**< 1: no dict ID will be saved into frame header (if dictionary compression) */ +} ZSTD_frameParameters; + +typedef struct { + ZSTD_compressionParameters cParams; + ZSTD_frameParameters fParams; +} ZSTD_parameters; + +/*= Custom memory allocation functions */ +typedef void* (*ZSTD_allocFunction) (void* opaque, size_t size); +typedef void (*ZSTD_freeFunction) (void* opaque, void* address); +typedef struct { ZSTD_allocFunction customAlloc; ZSTD_freeFunction customFree; void* opaque; } ZSTD_customMem; + + +/*************************************** +* Advanced compression functions +***************************************/ +/*! ZSTD_estimateCCtxSize() : + * Gives the amount of memory allocated for a ZSTD_CCtx given a set of compression parameters. + * `frameContentSize` is an optional parameter, provide `0` if unknown */ +ZSTDLIB_API size_t ZSTD_estimateCCtxSize(ZSTD_compressionParameters cParams); + +/*! ZSTD_createCCtx_advanced() : + * Create a ZSTD compression context using external alloc and free functions */ +ZSTDLIB_API ZSTD_CCtx* ZSTD_createCCtx_advanced(ZSTD_customMem customMem); + +/*! ZSTD_sizeofCCtx() : + * Gives the amount of memory used by a given ZSTD_CCtx */ +ZSTDLIB_API size_t ZSTD_sizeof_CCtx(const ZSTD_CCtx* cctx); + +/*! ZSTD_createCDict_byReference() : + * Create a digested dictionary for compression + * Dictionary content is simply referenced, and therefore stays in dictBuffer. + * It is important that dictBuffer outlives CDict, it must remain read accessible throughout the lifetime of CDict */ +ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict_byReference(const void* dictBuffer, size_t dictSize, int compressionLevel); + +/*! ZSTD_createCDict_advanced() : + * Create a ZSTD_CDict using external alloc and free, and customized compression parameters */ +ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict_advanced(const void* dict, size_t dictSize, unsigned byReference, + ZSTD_parameters params, ZSTD_customMem customMem); + +/*! ZSTD_sizeof_CDict() : + * Gives the amount of memory used by a given ZSTD_sizeof_CDict */ +ZSTDLIB_API size_t ZSTD_sizeof_CDict(const ZSTD_CDict* cdict); + +/*! ZSTD_getCParams() : +* @return ZSTD_compressionParameters structure for a selected compression level and estimated srcSize. +* `estimatedSrcSize` value is optional, select 0 if not known */ +ZSTDLIB_API ZSTD_compressionParameters ZSTD_getCParams(int compressionLevel, unsigned long long estimatedSrcSize, size_t dictSize); + +/*! ZSTD_getParams() : +* same as ZSTD_getCParams(), but @return a full `ZSTD_parameters` object instead of sub-component `ZSTD_compressionParameters`. +* All fields of `ZSTD_frameParameters` are set to default (0) */ +ZSTDLIB_API ZSTD_parameters ZSTD_getParams(int compressionLevel, unsigned long long estimatedSrcSize, size_t dictSize); + +/*! ZSTD_checkCParams() : +* Ensure param values remain within authorized range */ +ZSTDLIB_API size_t ZSTD_checkCParams(ZSTD_compressionParameters params); + +/*! ZSTD_adjustCParams() : +* optimize params for a given `srcSize` and `dictSize`. +* both values are optional, select `0` if unknown. */ +ZSTDLIB_API ZSTD_compressionParameters ZSTD_adjustCParams(ZSTD_compressionParameters cPar, unsigned long long srcSize, size_t dictSize); + +/*! ZSTD_compress_advanced() : +* Same as ZSTD_compress_usingDict(), with fine-tune control of each compression parameter */ +ZSTDLIB_API size_t ZSTD_compress_advanced (ZSTD_CCtx* ctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize, + const void* dict,size_t dictSize, + ZSTD_parameters params); + + +/*--- Advanced decompression functions ---*/ + +/*! ZSTD_isFrame() : + * Tells if the content of `buffer` starts with a valid Frame Identifier. + * Note : Frame Identifier is 4 bytes. If `size < 4`, @return will always be 0. + * Note 2 : Legacy Frame Identifiers are considered valid only if Legacy Support is enabled. + * Note 3 : Skippable Frame Identifiers are considered valid. */ +ZSTDLIB_API unsigned ZSTD_isFrame(const void* buffer, size_t size); + +/*! ZSTD_estimateDCtxSize() : + * Gives the potential amount of memory allocated to create a ZSTD_DCtx */ +ZSTDLIB_API size_t ZSTD_estimateDCtxSize(void); + +/*! ZSTD_createDCtx_advanced() : + * Create a ZSTD decompression context using external alloc and free functions */ +ZSTDLIB_API ZSTD_DCtx* ZSTD_createDCtx_advanced(ZSTD_customMem customMem); + +/*! ZSTD_sizeof_DCtx() : + * Gives the amount of memory used by a given ZSTD_DCtx */ +ZSTDLIB_API size_t ZSTD_sizeof_DCtx(const ZSTD_DCtx* dctx); + +/*! ZSTD_createDDict_byReference() : + * Create a digested dictionary, ready to start decompression operation without startup delay. + * Dictionary content is simply referenced, and therefore stays in dictBuffer. + * It is important that dictBuffer outlives DDict, it must remain read accessible throughout the lifetime of DDict */ +ZSTDLIB_API ZSTD_DDict* ZSTD_createDDict_byReference(const void* dictBuffer, size_t dictSize); + +ZSTDLIB_API ZSTD_DDict* ZSTD_createDDict_advanced(const void* dict, size_t dictSize, + unsigned byReference, ZSTD_customMem customMem); + +/*! ZSTD_sizeof_DDict() : + * Gives the amount of memory used by a given ZSTD_DDict */ +ZSTDLIB_API size_t ZSTD_sizeof_DDict(const ZSTD_DDict* ddict); + +/*! ZSTD_getDictID_fromDict() : + * Provides the dictID stored within dictionary. + * if @return == 0, the dictionary is not conformant with Zstandard specification. + * It can still be loaded, but as a content-only dictionary. */ +ZSTDLIB_API unsigned ZSTD_getDictID_fromDict(const void* dict, size_t dictSize); + +/*! ZSTD_getDictID_fromDDict() : + * Provides the dictID of the dictionary loaded into `ddict`. + * If @return == 0, the dictionary is not conformant to Zstandard specification, or empty. + * Non-conformant dictionaries can still be loaded, but as content-only dictionaries. */ +ZSTDLIB_API unsigned ZSTD_getDictID_fromDDict(const ZSTD_DDict* ddict); + +/*! ZSTD_getDictID_fromFrame() : + * Provides the dictID required to decompressed the frame stored within `src`. + * If @return == 0, the dictID could not be decoded. + * This could for one of the following reasons : + * - The frame does not require a dictionary to be decoded (most common case). + * - The frame was built with dictID intentionally removed. Whatever dictionary is necessary is a hidden information. + * Note : this use case also happens when using a non-conformant dictionary. + * - `srcSize` is too small, and as a result, the frame header could not be decoded (only possible if `srcSize < ZSTD_FRAMEHEADERSIZE_MAX`). + * - This is not a Zstandard frame. + * When identifying the exact failure cause, it's possible to used ZSTD_getFrameParams(), which will provide a more precise error code. */ +ZSTDLIB_API unsigned ZSTD_getDictID_fromFrame(const void* src, size_t srcSize); + + +/******************************************************************** +* Advanced streaming functions +********************************************************************/ + +/*===== Advanced Streaming compression functions =====*/ +ZSTDLIB_API ZSTD_CStream* ZSTD_createCStream_advanced(ZSTD_customMem customMem); +ZSTDLIB_API size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pledgedSrcSize); /**< pledgedSrcSize must be correct */ +ZSTDLIB_API size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t dictSize, int compressionLevel); +ZSTDLIB_API size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs, const void* dict, size_t dictSize, + ZSTD_parameters params, unsigned long long pledgedSrcSize); /**< pledgedSrcSize is optional and can be zero == unknown */ +ZSTDLIB_API size_t ZSTD_initCStream_usingCDict(ZSTD_CStream* zcs, const ZSTD_CDict* cdict); /**< note : cdict will just be referenced, and must outlive compression session */ +ZSTDLIB_API size_t ZSTD_resetCStream(ZSTD_CStream* zcs, unsigned long long pledgedSrcSize); /**< re-use compression parameters from previous init; skip dictionary loading stage; zcs must be init at least once before */ +ZSTDLIB_API size_t ZSTD_sizeof_CStream(const ZSTD_CStream* zcs); + + +/*===== Advanced Streaming decompression functions =====*/ +typedef enum { ZSTDdsp_maxWindowSize } ZSTD_DStreamParameter_e; +ZSTDLIB_API ZSTD_DStream* ZSTD_createDStream_advanced(ZSTD_customMem customMem); +ZSTDLIB_API size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t dictSize); +ZSTDLIB_API size_t ZSTD_setDStreamParameter(ZSTD_DStream* zds, ZSTD_DStreamParameter_e paramType, unsigned paramValue); +ZSTDLIB_API size_t ZSTD_initDStream_usingDDict(ZSTD_DStream* zds, const ZSTD_DDict* ddict); /**< note : ddict will just be referenced, and must outlive decompression session */ +ZSTDLIB_API size_t ZSTD_resetDStream(ZSTD_DStream* zds); /**< re-use decompression parameters from previous init; saves dictionary loading */ +ZSTDLIB_API size_t ZSTD_sizeof_DStream(const ZSTD_DStream* zds); + + +/********************************************************************* +* Buffer-less and synchronous inner streaming functions +* +* This is an advanced API, giving full control over buffer management, for users which need direct control over memory. +* But it's also a complex one, with many restrictions (documented below). +* Prefer using normal streaming API for an easier experience +********************************************************************* */ + +/** + Buffer-less streaming compression (synchronous mode) + + A ZSTD_CCtx object is required to track streaming operations. + Use ZSTD_createCCtx() / ZSTD_freeCCtx() to manage resource. + ZSTD_CCtx object can be re-used multiple times within successive compression operations. + + Start by initializing a context. + Use ZSTD_compressBegin(), or ZSTD_compressBegin_usingDict() for dictionary compression, + or ZSTD_compressBegin_advanced(), for finer parameter control. + It's also possible to duplicate a reference context which has already been initialized, using ZSTD_copyCCtx() + + Then, consume your input using ZSTD_compressContinue(). + There are some important considerations to keep in mind when using this advanced function : + - ZSTD_compressContinue() has no internal buffer. It uses externally provided buffer only. + - Interface is synchronous : input is consumed entirely and produce 1+ (or more) compressed blocks. + - Caller must ensure there is enough space in `dst` to store compressed data under worst case scenario. + Worst case evaluation is provided by ZSTD_compressBound(). + ZSTD_compressContinue() doesn't guarantee recover after a failed compression. + - ZSTD_compressContinue() presumes prior input ***is still accessible and unmodified*** (up to maximum distance size, see WindowLog). + It remembers all previous contiguous blocks, plus one separated memory segment (which can itself consists of multiple contiguous blocks) + - ZSTD_compressContinue() detects that prior input has been overwritten when `src` buffer overlaps. + In which case, it will "discard" the relevant memory section from its history. + + Finish a frame with ZSTD_compressEnd(), which will write the last block(s) and optional checksum. + It's possible to use a NULL,0 src content, in which case, it will write a final empty block to end the frame, + Without last block mark, frames will be considered unfinished (broken) by decoders. + + You can then reuse `ZSTD_CCtx` (ZSTD_compressBegin()) to compress some new frame. +*/ + +/*===== Buffer-less streaming compression functions =====*/ +ZSTDLIB_API size_t ZSTD_compressBegin(ZSTD_CCtx* cctx, int compressionLevel); +ZSTDLIB_API size_t ZSTD_compressBegin_usingDict(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel); +ZSTDLIB_API size_t ZSTD_compressBegin_advanced(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, ZSTD_parameters params, unsigned long long pledgedSrcSize); +ZSTDLIB_API size_t ZSTD_copyCCtx(ZSTD_CCtx* cctx, const ZSTD_CCtx* preparedCCtx, unsigned long long pledgedSrcSize); +ZSTDLIB_API size_t ZSTD_compressContinue(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); +ZSTDLIB_API size_t ZSTD_compressEnd(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); + + + +/*- + Buffer-less streaming decompression (synchronous mode) + + A ZSTD_DCtx object is required to track streaming operations. + Use ZSTD_createDCtx() / ZSTD_freeDCtx() to manage it. + A ZSTD_DCtx object can be re-used multiple times. + + First typical operation is to retrieve frame parameters, using ZSTD_getFrameParams(). + It fills a ZSTD_frameParams structure which provide important information to correctly decode the frame, + such as the minimum rolling buffer size to allocate to decompress data (`windowSize`), + and the dictionary ID used. + (Note : content size is optional, it may not be present. 0 means : content size unknown). + Note that these values could be wrong, either because of data malformation, or because an attacker is spoofing deliberate false information. + As a consequence, check that values remain within valid application range, especially `windowSize`, before allocation. + Each application can set its own limit, depending on local restrictions. For extended interoperability, it is recommended to support at least 8 MB. + Frame parameters are extracted from the beginning of the compressed frame. + Data fragment must be large enough to ensure successful decoding, typically `ZSTD_frameHeaderSize_max` bytes. + @result : 0 : successful decoding, the `ZSTD_frameParams` structure is correctly filled. + >0 : `srcSize` is too small, please provide at least @result bytes on next attempt. + errorCode, which can be tested using ZSTD_isError(). + + Start decompression, with ZSTD_decompressBegin() or ZSTD_decompressBegin_usingDict(). + Alternatively, you can copy a prepared context, using ZSTD_copyDCtx(). + + Then use ZSTD_nextSrcSizeToDecompress() and ZSTD_decompressContinue() alternatively. + ZSTD_nextSrcSizeToDecompress() tells how many bytes to provide as 'srcSize' to ZSTD_decompressContinue(). + ZSTD_decompressContinue() requires this _exact_ amount of bytes, or it will fail. + + @result of ZSTD_decompressContinue() is the number of bytes regenerated within 'dst' (necessarily <= dstCapacity). + It can be zero, which is not an error; it just means ZSTD_decompressContinue() has decoded some metadata item. + It can also be an error code, which can be tested with ZSTD_isError(). + + ZSTD_decompressContinue() needs previous data blocks during decompression, up to `windowSize`. + They should preferably be located contiguously, prior to current block. + Alternatively, a round buffer of sufficient size is also possible. Sufficient size is determined by frame parameters. + ZSTD_decompressContinue() is very sensitive to contiguity, + if 2 blocks don't follow each other, make sure that either the compressor breaks contiguity at the same place, + or that previous contiguous segment is large enough to properly handle maximum back-reference. + + A frame is fully decoded when ZSTD_nextSrcSizeToDecompress() returns zero. + Context can then be reset to start a new decompression. + + Note : it's possible to know if next input to present is a header or a block, using ZSTD_nextInputType(). + This information is not required to properly decode a frame. + + == Special case : skippable frames == + + Skippable frames allow integration of user-defined data into a flow of concatenated frames. + Skippable frames will be ignored (skipped) by a decompressor. The format of skippable frames is as follows : + a) Skippable frame ID - 4 Bytes, Little endian format, any value from 0x184D2A50 to 0x184D2A5F + b) Frame Size - 4 Bytes, Little endian format, unsigned 32-bits + c) Frame Content - any content (User Data) of length equal to Frame Size + For skippable frames ZSTD_decompressContinue() always returns 0. + For skippable frames ZSTD_getFrameParams() returns fparamsPtr->windowLog==0 what means that a frame is skippable. + It also returns Frame Size as fparamsPtr->frameContentSize. +*/ + +typedef struct { + unsigned long long frameContentSize; + unsigned windowSize; + unsigned dictID; + unsigned checksumFlag; +} ZSTD_frameParams; + +/*===== Buffer-less streaming decompression functions =====*/ +ZSTDLIB_API size_t ZSTD_getFrameParams(ZSTD_frameParams* fparamsPtr, const void* src, size_t srcSize); /**< doesn't consume input, see details below */ +ZSTDLIB_API size_t ZSTD_decompressBegin(ZSTD_DCtx* dctx); +ZSTDLIB_API size_t ZSTD_decompressBegin_usingDict(ZSTD_DCtx* dctx, const void* dict, size_t dictSize); +ZSTDLIB_API void ZSTD_copyDCtx(ZSTD_DCtx* dctx, const ZSTD_DCtx* preparedDCtx); +ZSTDLIB_API size_t ZSTD_nextSrcSizeToDecompress(ZSTD_DCtx* dctx); +ZSTDLIB_API size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); +typedef enum { ZSTDnit_frameHeader, ZSTDnit_blockHeader, ZSTDnit_block, ZSTDnit_lastBlock, ZSTDnit_checksum, ZSTDnit_skippableFrame } ZSTD_nextInputType_e; +ZSTDLIB_API ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx* dctx); + +/** + Block functions + + Block functions produce and decode raw zstd blocks, without frame metadata. + Frame metadata cost is typically ~18 bytes, which can be non-negligible for very small blocks (< 100 bytes). + User will have to take in charge required information to regenerate data, such as compressed and content sizes. + + A few rules to respect : + - Compressing and decompressing require a context structure + + Use ZSTD_createCCtx() and ZSTD_createDCtx() + - It is necessary to init context before starting + + compression : ZSTD_compressBegin() + + decompression : ZSTD_decompressBegin() + + variants _usingDict() are also allowed + + copyCCtx() and copyDCtx() work too + - Block size is limited, it must be <= ZSTD_getBlockSizeMax() + + If you need to compress more, cut data into multiple blocks + + Consider using the regular ZSTD_compress() instead, as frame metadata costs become negligible when source size is large. + - When a block is considered not compressible enough, ZSTD_compressBlock() result will be zero. + In which case, nothing is produced into `dst`. + + User must test for such outcome and deal directly with uncompressed data + + ZSTD_decompressBlock() doesn't accept uncompressed data as input !!! + + In case of multiple successive blocks, decoder must be informed of uncompressed block existence to follow proper history. + Use ZSTD_insertBlock() in such a case. +*/ + +#define ZSTD_BLOCKSIZE_ABSOLUTEMAX (128 * 1024) /* define, for static allocation */ +/*===== Raw zstd block functions =====*/ +ZSTDLIB_API size_t ZSTD_getBlockSizeMax(ZSTD_CCtx* cctx); +ZSTDLIB_API size_t ZSTD_compressBlock (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); +ZSTDLIB_API size_t ZSTD_decompressBlock(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); +ZSTDLIB_API size_t ZSTD_insertBlock(ZSTD_DCtx* dctx, const void* blockStart, size_t blockSize); /**< insert block into `dctx` history. Useful for uncompressed blocks */ + + +#endif /* ZSTD_H_ZSTD_STATIC_LINKING_ONLY */ + +#if defined (__cplusplus) +} +#endif diff --git a/lib/libzstd.lib b/lib/libzstd.lib new file mode 100644 index 0000000..630ef99 Binary files /dev/null and b/lib/libzstd.lib differ diff --git a/lib/libzstdd.lib b/lib/libzstdd.lib new file mode 100644 index 0000000..dae1ca8 Binary files /dev/null and b/lib/libzstdd.lib differ diff --git a/src/LiveScanClient/frameFileWriterReader.cpp b/src/LiveScanClient/frameFileWriterReader.cpp new file mode 100644 index 0000000..d13b9ef --- /dev/null +++ b/src/LiveScanClient/frameFileWriterReader.cpp @@ -0,0 +1,109 @@ +#include "frameFileWriterReader.h" + +#include + +FrameFileWriterReader::FrameFileWriterReader() +{ + +} + +void FrameFileWriterReader::closeFileIfOpened() +{ + if (m_pFileHandle == nullptr) + return; + + fclose(m_pFileHandle); + m_pFileHandle = nullptr; + m_bFileOpenedForReading = false; + m_bFileOpenedForWriting = false; +} + +void FrameFileWriterReader::resetTimer() +{ + recording_start_time = std::chrono::steady_clock::now(); +} + +int FrameFileWriterReader::getRecordingTimeMilliseconds() +{ + std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now(); + return static_cast(std::chrono::duration_cast(end - recording_start_time).count()); +} + +void FrameFileWriterReader::openCurrentFileForReading() +{ + closeFileIfOpened(); + + m_pFileHandle = fopen(m_sFilename.c_str(), "rb"); + + m_bFileOpenedForReading = true; + m_bFileOpenedForWriting = false; +} + +void FrameFileWriterReader::openNewFileForWriting() +{ + closeFileIfOpened(); + + char filename[1024]; + time_t t = time(0); + struct tm * now = localtime(&t); + sprintf(filename, "recording_%04d_%02d_%02d_%02d_%02d.bin", now->tm_year, now->tm_mon, now->tm_mday, now->tm_hour, now->tm_min, now->tm_sec); + m_sFilename = filename; + m_pFileHandle = fopen(filename, "wb"); + + m_bFileOpenedForReading = false; + m_bFileOpenedForWriting = true; + + resetTimer(); +} + +bool FrameFileWriterReader::readFrame(std::vector &outPoints, std::vector &outColors) +{ + if (!m_bFileOpenedForReading) + openCurrentFileForReading(); + + outPoints.clear(); + outColors.clear(); + FILE *f = m_pFileHandle; + int nPoints, timestamp; + char tmp[1024]; + int nread = fscanf_s(f, "%s %d %s %d", tmp, 1024, &nPoints, tmp, 1024, ×tamp); + + if (nread < 4) + return false; + + if (nPoints == 0) + return true; + + fgetc(f); // '\n' + outPoints.resize(nPoints); + outColors.resize(nPoints); + + fread((void*)outPoints.data(), sizeof(outPoints[0]), nPoints, f); + fread((void*)outColors.data(), sizeof(outColors[0]), nPoints, f); + fgetc(f); // '\n' + return true; + +} + + +void FrameFileWriterReader::writeFrame(std::vector points, std::vector colors) +{ + if (!m_bFileOpenedForWriting) + openNewFileForWriting(); + + FILE *f = m_pFileHandle; + + int nPoints = static_cast(points.size()); + fprintf(f, "n_points= %d\nframe_timestamp= %d\n", nPoints, getRecordingTimeMilliseconds()); + if (nPoints > 0) + { + fwrite((void*)points.data(), sizeof(points[0]), nPoints, f); + fwrite((void*)colors.data(), sizeof(colors[0]), nPoints, f); + } + fprintf(f, "\n"); +} + +FrameFileWriterReader::~FrameFileWriterReader() +{ + closeFileIfOpened(); +} \ No newline at end of file diff --git a/src/LiveScanClient/liveScanClient.cpp b/src/LiveScanClient/liveScanClient.cpp index 96e2b8a..1eaa269 100644 --- a/src/LiveScanClient/liveScanClient.cpp +++ b/src/LiveScanClient/liveScanClient.cpp @@ -19,6 +19,7 @@ #include #include #include +#include "zstd.h" std::mutex m_mSocketThreadMutex; @@ -52,11 +53,14 @@ LiveScanClient::LiveScanClient() : m_bFilter(false), m_bStreamOnlyBodies(false), m_bCaptureFrame(false), + m_bCaptureToFile(true), m_bConnected(false), m_bConfirmCaptured(false), m_bConfirmCalibrated(false), m_bShowDepth(false), m_bSocketThread(true), + m_bFrameCompression(true), + m_iCompressionLevel(2), m_pClientSocket(NULL), m_nFilterNeighbors(10), m_fFilterThreshold(0.01f) @@ -130,7 +134,7 @@ int LiveScanClient::Run(HINSTANCE hInstance, int nCmdShow) MSG msg = {0}; WNDCLASS wc; - // Dialog custom window class + // Dialog custom window class ZeroMemory(&wc, sizeof(wc)); wc.style = CS_HREDRAW | CS_VREDRAW; wc.cbWndExtra = DLGWINDOWEXTRA; @@ -180,6 +184,8 @@ int LiveScanClient::Run(HINSTANCE hInstance, int nCmdShow) return static_cast(msg.wParam); } + + void LiveScanClient::UpdateFrame() { if (!pCapture->bInitialized) @@ -200,10 +206,19 @@ void LiveScanClient::UpdateFrame() if (m_bCaptureFrame) { - m_vGatheredVertices.push_back(m_vLastFrameVertices); - m_vGatheredRGBPoints.push_back(m_vLastFrameRGB); - m_bConfirmCaptured = true; - m_bCaptureFrame = false; + if (m_bCaptureToFile) + { + m_framesFileWriterReader.writeFrame(m_vLastFrameVertices, m_vLastFrameRGB); + m_bConfirmCaptured = true; + m_bCaptureFrame = false; + } + else + { + m_vGatheredVertices.push_back(m_vLastFrameVertices); + m_vGatheredRGBPoints.push_back(m_vLastFrameRGB); + m_bConfirmCaptured = true; + m_bCaptureFrame = false; + } } } @@ -499,28 +514,45 @@ void LiveScanClient::HandleSocket() } m_bStreamOnlyBodies = (received[i] != 0); - i++; + i += 1; - //so that we do not lose the next character in the stream - i--; + m_iCompressionLevel = *(int*)(received.c_str() + i); + if (m_iCompressionLevel > 0) + m_bFrameCompression = true; + else + m_bFrameCompression = false; } //send stored frame else if (received[i] == MSG_REQUEST_STORED_FRAME) { byteToSend = MSG_STORED_FRAME; m_pClientSocket->SendBytes(&byteToSend, 1); - - if (m_vGatheredRGBPoints.size() > 0) + if (m_bCaptureToFile) { - SendFrame(m_vGatheredVertices[0], m_vGatheredRGBPoints[0], m_vLastFrameBody); - - m_vGatheredRGBPoints.erase(m_vGatheredRGBPoints.begin(), m_vGatheredRGBPoints.begin() + 1); - m_vGatheredVertices.erase(m_vGatheredVertices.begin(), m_vGatheredVertices.begin() + 1); + vector points; + vector colors; + bool res = m_framesFileWriterReader.readFrame(points, colors); + if (res == false) + { + int size = -1; + m_pClientSocket->SendBytes((char*)&size, 4); + } else + SendFrame(points, colors, m_vLastFrameBody); } else { - int size = -1; - m_pClientSocket->SendBytes((char*)&size, 4); + if (m_vGatheredRGBPoints.size() > 0) + { + SendFrame(m_vGatheredVertices[0], m_vGatheredRGBPoints[0], m_vLastFrameBody); + + m_vGatheredRGBPoints.erase(m_vGatheredRGBPoints.begin(), m_vGatheredRGBPoints.begin() + 1); + m_vGatheredVertices.erase(m_vGatheredVertices.begin(), m_vGatheredVertices.begin() + 1); + } + else + { + int size = -1; + m_pClientSocket->SendBytes((char*)&size, 4); + } } } //send last frame @@ -634,7 +666,7 @@ void LiveScanClient::SendFrame(vector vertices, vector RGB, vector ptr2 += sizeof(short) * 3; pos += sizeof(short) * 3; } - + int nBodies = body.size(); size += sizeof(nBodies); for (int i = 0; i < nBodies; i++) @@ -682,9 +714,24 @@ void LiveScanClient::SendFrame(vector vertices, vector RGB, vector } } - m_pClientSocket->SendBytes((char*)&size, sizeof(int)); - m_pClientSocket->SendBytes(buffer.data(), size); + int iCompression = static_cast(m_bFrameCompression); + if (m_bFrameCompression) + { + // *2, because according to zstd documentation, increasing the size of the output buffer above a + // bound should speed up the compression. + int cBuffSize = ZSTD_compressBound(size) * 2; + vector compressedBuffer(cBuffSize); + int cSize = ZSTD_compress(compressedBuffer.data(), cBuffSize, buffer.data(), size, m_iCompressionLevel); + size = cSize; + buffer = compressedBuffer; + } + char header[8]; + memcpy(header, (char*)&size, sizeof(size)); + memcpy(header + 4, (char*)&iCompression, sizeof(iCompression)); + + m_pClientSocket->SendBytes((char*)&header, sizeof(int) * 2); + m_pClientSocket->SendBytes(buffer.data(), size); } void LiveScanClient::StoreFrame(Point3f *vertices, Point2f *mapping, RGB *color, vector &bodies, BYTE* bodyIndex)