using LZ4Sharp; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Sockets; using System.Threading; using UnityEngine; namespace com.rfilkov.kinect { /// /// NetClientInterface is a sensor-interface that receives the sensor data over the network instead from a connected device. /// public class NetClientInterface : DepthSensorBase { [Tooltip("Whether to broadcast, in order to find automatically the first available server (local network only).")] public bool autoServerDiscovery = true; [Tooltip("Host name or IP address of the network server.")] public string serverHost = "localhost"; [Tooltip("The base port for all server frame streams (each frame stream listens on separate port).")] public int serverBasePort = 11000; [Tooltip("Whether to get color frames from the server, if required by KinectManager.")] public bool getColorFrames = true; [Tooltip("Whether to get depth frames from the server, if required by KinectManager.")] public bool getDepthFrames = true; [Tooltip("Whether to get infrared frames from the server, if required by KinectManager.")] public bool getInfraredFrames = true; [Tooltip("Whether to get body-data frames from the server, if required by KinectManager.")] public bool getBodyFrames = true; [Tooltip("Whether to get body-index frames from the server, if required by KinectManager.")] public bool getBodyIndexFrames = true; [Tooltip("Whether to get sensor-pose frames from the server, if required by KinectManager.")] public bool getPoseFrames = true; [Tooltip("UI-Text to display client status messages.")] public UnityEngine.UI.Text clientStatusText; // sensor frame servers private TcpNetClient controlFrameClient = null; private TcpNetClient colorFrameClient = null; private TcpNetClient depthFrameClient = null; private TcpNetClient infraredFrameClient = null; private TcpNetClient bodyDataFrameClient = null; private TcpNetClient bodyIndexFrameClient = null; private TcpNetClient poseFrameClient = null; private TcpNetClient depth2colorFrameClient = null; private TcpNetClient color2depthFrameClient = null; private TcpNetClient color2infraredFrameClient = null; private TcpNetClient color2bodyIndexFrameClient = null; // sensor data private KinectInterop.SensorData sensorData = null; // control frame private KinectInterop.NetSensorData netSensorData = null; //private ulong lastControlFrameTime = 0; //private bool bSensorDataMsgSent = false; private bool bGotNetSensorData = false; private bool bSetNetSensorData = false; private bool bInvalidSensorData = false; private bool bGotDST = false; private bool bGotCST = false; // thread sleep time in milliseconds, to wait for server response private const int THREAD_WAIT_TIME_MS = 100; // keep-alive private ulong lastKeepAliveFrameTime = 0; private const int keepAliveInterval = 20000000; // 2 seconds // disconnect-reconnect private ulong latestDataReceivedAt = 0; private const int disconnectAfter = 300000000; // 10 seconds private ulong disconnectedAt = 0; private const int reconnectAfter = 50000000; // 5 seconds // color image private byte[] colorImageData = null; private ulong lastColorImageTime = 0; // infrared image private int infraredImageWidth = 0, infraredImageHeight = 0; private byte[] infraredImageData = null; private ulong lastInfraredImageTime = 0; private Texture2D infraredImageTex = null; // data frame times private ulong lastControlFrameTime = 0; private ulong lastColorFrameTime = 0; private ulong lastDepthFrameTime = 0; private ulong lastInfraredFrameTime = 0; private ulong lastBodyDataFrameTime = 0; private ulong lastBodyIndexFrameTime = 0; private ulong lastPoseFrameTime = 0; private ulong lastDepth2colorFrameTime = 0; private ulong lastColor2depthFrameTime = 0; private ulong lastColor2infraredFrameTime = 0; private ulong lastColor2bodyIndexFrameTime = 0; // body frame private bool bIgnoreZcoords = false; // pose frame private KinectInterop.NetPoseData netPoseData = null; private object netPoseLock = new object(); // depth2color frame private int depth2colorImageWidth = 0, depth2colorImageHeight = 0; private byte[] depth2colorImageData = null; private ulong lastDepth2ColorImageTime = 0; // color2infrared frame private int color2infraredImageWidth = 0, color2infraredImageHeight = 0; private byte[] color2infraredImageData = null; private ulong lastColor2InfraredImageTime = 0; private Texture2D colorInfraredTexture = null; // decompressors private ILZ4Decompressor controlFrameDecompressor = null; private ILZ4Decompressor depthFrameDecompressor = null; private ILZ4Decompressor bodyIndexFrameDecompressor = null; private ILZ4Decompressor color2depthFrameDecompressor = null; private ILZ4Decompressor color2bodyIndexFrameDecompressor = null; // console buffer private System.Text.StringBuilder sbConsole = new System.Text.StringBuilder(); // whether the server broadcast response is received private bool bBroadcastResponseReceived = false; // frame synchronization private bool isFrameSyncNeeded = false; private Dictionary> dictNetMessageData = null; private List alNetMessageTime = null; private object syncMessageLock = new object(); // depth sensor settings [System.Serializable] public class NetSensorSettings : DepthSensorBase.BaseSensorSettings { public string serverHost; public int serverBasePort; public bool getBodyIndexFrames; } public override KinectInterop.DepthSensorPlatform GetSensorPlatform() { return KinectInterop.DepthSensorPlatform.NetSensor; } public override System.Type GetSensorSettingsType() { return typeof(NetSensorSettings); } public override BaseSensorSettings GetSensorSettings(BaseSensorSettings settings) { if (settings == null) { settings = new NetSensorSettings(); } NetSensorSettings extSettings = (NetSensorSettings)base.GetSensorSettings(settings); extSettings.serverHost = serverHost; extSettings.serverBasePort = serverBasePort; extSettings.getBodyIndexFrames = getBodyIndexFrames; return settings; } public override void SetSensorSettings(BaseSensorSettings settings) { if (settings == null) return; base.SetSensorSettings(settings); NetSensorSettings extSettings = (NetSensorSettings)settings; serverHost = extSettings.serverHost; serverBasePort = extSettings.serverBasePort; getBodyIndexFrames = extSettings.getBodyIndexFrames; } public override List GetAvailableSensors() { List alSensorInfo = new List(); KinectInterop.SensorDeviceInfo sensorInfo = new KinectInterop.SensorDeviceInfo(); sensorInfo.sensorId = serverHost + ":" + serverBasePort + ", discovery: " + autoServerDiscovery; sensorInfo.sensorName = "NetSensor"; sensorInfo.sensorCaps = KinectInterop.FrameSource.TypeAll; if (consoleLogMessages) Debug.Log(string.Format(" D{0}: {1}, id: {2}", 0, sensorInfo.sensorName, sensorInfo.sensorId)); alSensorInfo.Add(sensorInfo); return alSensorInfo; } public override KinectInterop.SensorData OpenSensor(KinectManager kinectManager, KinectInterop.FrameSource dwFlags, bool bSyncDepthAndColor, bool bSyncBodyAndDepth) { // save initial parameters base.OpenSensor(kinectManager, dwFlags, bSyncDepthAndColor, bSyncBodyAndDepth); if (deviceStreamingMode == KinectInterop.DeviceStreamingMode.PlayRecording) { Debug.LogWarning("Playback selected, but this is not applicable to the network sensor. Ignoring..."); } //List alSensors = GetAvailableSensors(); //if (deviceIndex >= alSensors.Count) //{ // Debug.Log(" D" + deviceIndex + " is not available. You can set the device index to -1, to disable it."); // return null; //} sensorData = new KinectInterop.SensorData(); sensorData.sensorIntPlatform = KinectInterop.DepthSensorPlatform.NetSensor; sensorData.sensorId = "NetSensor" + "_" + serverHost + "_" + serverBasePort; sensorData.sensorName = "NetSensor"; sensorData.sensorCaps = KinectInterop.FrameSource.TypeAll; // flip color & depth image vertically sensorData.colorImageScale = new Vector3(-1f, -1f, 1f); sensorData.depthImageScale = new Vector3(-1f, -1f, 1f); sensorData.infraredImageScale = new Vector3(-1f, -1f, 1f); sensorData.sensorSpaceScale = new Vector3(-1f, -1f, 1f); sensorData.unitToMeterFactor = 1f; // depth camera offset & matrix z-flip sensorRotOffset = new Vector3(0f, 0f, 0f); sensorRotFlipZ = true; sensorRotIgnoreY = true; // color camera data & intrinsics sensorData.colorImageFormat = TextureFormat.RGB24; sensorData.colorImageStride = 3; // 3 bytes per pixel // init network clients InitNetClients(dwFlags); // check for frame sync isFrameSyncNeeded = bSyncDepthAndColor || bSyncBodyAndDepth; if(isFrameSyncNeeded) { dictNetMessageData = new Dictionary>(); alNetMessageTime = new List(); } if (consoleLogMessages) Debug.Log("D" + deviceIndex + " NetSensor opened: " + serverHost + ":" + serverBasePort + ", discovery: " + autoServerDiscovery); return sensorData; } public override void CloseSensor(KinectInterop.SensorData sensorData) { // close network clients CloseNetClients(); CleanupSyncMessageData(); // close opened resources base.CloseSensor(sensorData); // finish frame sync if(isFrameSyncNeeded) { isFrameSyncNeeded = false; dictNetMessageData.Clear(); dictNetMessageData = null; alNetMessageTime.Clear(); alNetMessageTime = null; } if (consoleLogMessages) Debug.Log("D" + deviceIndex + " NetSensor closed: " + serverHost + ":" + serverBasePort + ", discovery: " + autoServerDiscovery); } public override bool IsSensorDataValid() { if(bInvalidSensorData) { // got invalid sensor-data once return false; } // wait for net-cst long timeStart = System.DateTime.Now.Ticks; long timeNow = timeStart; while (!bGotNetSensorData && (timeNow - timeStart) < 50000000) // timeout - 5 seconds { Thread.Sleep(THREAD_WAIT_TIME_MS); timeNow = System.DateTime.Now.Ticks; } if (bGotNetSensorData && !bSetNetSensorData && netSensorData != null) { SetNetSensorData(netSensorData, sensorData, KinectManager.Instance); bSetNetSensorData = true; netSensorData = null; } bInvalidSensorData = !bGotNetSensorData; return bGotNetSensorData; } public override bool UpdateSensorData(KinectInterop.SensorData sensorData, KinectManager kinectManager, bool isPlayMode) { // client status text if (sbConsole.Length > 0) { lock (sbConsole) { if (clientStatusText) clientStatusText.text = sbConsole.ToString(); sbConsole.Clear(); } } // check for server timeout ulong ulTimeNow = (ulong)System.DateTime.Now.Ticks; bool isControlClientActive = controlFrameClient != null ? controlFrameClient.IsActive() : false; latestDataReceivedAt = GetLatestTimestamp(); if(latestDataReceivedAt != 0 && (ulTimeNow - latestDataReceivedAt) >= disconnectAfter && isControlClientActive) { Debug.LogWarning("Server timeout detected. Disconnecting..."); disconnectedAt = ulTimeNow; CloseNetClients(); CleanupSyncMessageData(); return true; } // check for server disconnection if (disconnectedAt == 0 && controlFrameClient != null && !isControlClientActive) { Debug.LogError("Server disconnection detected."); CloseNetClients(); CleanupSyncMessageData(); disconnectedAt = ulTimeNow; } // try to reconnect if(disconnectedAt != 0 && (ulTimeNow - disconnectedAt) >= reconnectAfter) { if (consoleLogMessages) Debug.Log("Start reconnecting..."); CloseNetClients(); CleanupSyncMessageData(); InitNetClients(frameSourceFlags); return true; } // send control messages as needed UpdateSendControlMessages(ulTimeNow, isControlClientActive, kinectManager); // color frame if (colorImageData != null && sensorData.lastColorFrameTime != lastColorImageTime && !isPlayMode) { if (sensorData.colorImageTexture == null && sensorData.colorImageWidth > 0 && sensorData.colorImageHeight > 0) { sensorData.colorImageTexture = new Texture2D(sensorData.colorImageWidth, sensorData.colorImageHeight, TextureFormat.RGB24, false); sensorData.colorImageTexture.wrapMode = TextureWrapMode.Clamp; sensorData.colorImageTexture.filterMode = FilterMode.Point; } lock (colorFrameLock) { Texture2D colorImageTex2D = (Texture2D)sensorData.colorImageTexture; if (colorImageTex2D != null) { colorImageTex2D.LoadImage(colorImageData); colorImageTex2D.Apply(); } sensorData.lastColorFrameTime = currentColorTimestamp = rawColorTimestamp = lastColorImageTime; //Debug.Log("UpdateColorTimestamp: " + lastColorImageTime); } } // check for depth texture if((sensorData.depthImageTexture == null && sensorData.depthImage != null && kinectManager.getDepthFrames == KinectManager.DepthTextureType.DepthTexture) || (sensorData.bodyImageTexture == null && sensorData.bodyIndexImage != null && (kinectManager.getBodyFrames == KinectManager.BodyTextureType.UserTexture || kinectManager.getBodyFrames == KinectManager.BodyTextureType.BodyTexture))) { KinectInterop.InitSensorData(sensorData, kinectManager); } // infrared frame if(infraredImageData != null && sensorData.lastInfraredImageTime != lastInfraredImageTime && !isPlayMode) { if (sensorData.infraredImageTexture == null && infraredImageWidth > 0 && infraredImageHeight > 0) { infraredImageTex = new Texture2D(infraredImageWidth, infraredImageHeight, TextureFormat.RGB24, false); infraredImageTex.wrapMode = TextureWrapMode.Clamp; infraredImageTex.filterMode = FilterMode.Point; sensorData.infraredImageTexture = KinectInterop.CreateRenderTexture(sensorData.infraredImageTexture, infraredImageWidth, infraredImageHeight); } lock (infraredFrameLock) { if (infraredImageTex != null) { infraredImageTex.LoadImage(infraredImageData); infraredImageTex.Apply(); Graphics.Blit(infraredImageTex, sensorData.infraredImageTexture); } sensorData.lastInfraredImageTime = lastInfraredImageTime; //Debug.Log("UpdateInfraredImageTimestamp: " + lastInfraredImageTime); } } // pose frame if (netPoseData != null && sensorData.lastSensorPoseFrameTime != netPoseData.sensorPoseTime && !isPlayMode) { lock(netPoseLock) { SetSensorNetPoseData(netPoseData, sensorData, kinectManager); ApplySensorPoseUpdate(kinectManager); } } // get KM setting for processing of body data bIgnoreZcoords = kinectManager.ignoreZCoordinates; // process the other sensor data return base.UpdateSensorData(sensorData, kinectManager, isPlayMode); } // send the control messages as needed private void UpdateSendControlMessages(ulong ulTimeNow, bool isControlClientActive, KinectManager kinectManager) { //// control - get sensor data //if (!bSensorDataMsgSent && isControlClientActive) //{ // SendControlMessage(ControlMessageType.GetSensorData); // bSensorDataMsgSent = true; // lastKeepAliveFrameTime = ulTimeNow; //} // control - set-sensor-data if (netSensorData != null) { SetNetSensorData(netSensorData, sensorData, kinectManager); bSetNetSensorData = true; netSensorData = null; } // control - keep alive if ((ulTimeNow - lastKeepAliveFrameTime) >= keepAliveInterval && isControlClientActive) { lastKeepAliveFrameTime = ulTimeNow; SendControlMessage(ControlMessageType.KeepAlive); } } // unprojects plane point into the space public override Vector3 UnprojectPoint(KinectInterop.CameraIntrinsics intr, Vector2 pixel, float depth) { Vector3 point = Vector3.zero; if (intr == null || depth <= 0f) return point; if(sensorPlatform == KinectInterop.DepthSensorPlatform.Kinect4Azure || sensorPlatform == KinectInterop.DepthSensorPlatform.DummyK4A) { depth = depth * 1000f; } int di = (int)(pixel.x + 0.5f) + (int)(pixel.y + 0.5f) * intr.width; if (intr.cameraType == 0) // depth { if (depth2SpaceTable == null) { GetDepthCameraSpaceTable(sensorData); } if(depth2SpaceTable != null && di >= 0 && di < depth2SpaceTable.Length) { point = depth2SpaceTable[di] * depth; } } else if(intr.cameraType == 1) // color { if (color2SpaceTable == null) { GetColorCameraSpaceTable(sensorData); } if (color2SpaceTable != null && di >= 0 && di < color2SpaceTable.Length) { point = color2SpaceTable[di] * depth; } } return point; } // projects space point onto a plane public override Vector2 ProjectPoint(KinectInterop.CameraIntrinsics intr, Vector3 point) { if (intr == null || point == Vector3.zero) return Vector2.zero; float x = point.x / point.z; float y = point.y / point.z; Vector2 pixel = new Vector2(x * intr.fx + intr.ppx, y * intr.fy + intr.ppy); return pixel; } // transforms a point from one space to another public override Vector3 TransformPoint(KinectInterop.CameraExtrinsics extr, Vector3 point) { if (extr == null) return Vector3.zero; float toPointX = 0f, toPointY = 0f, toPointZ = 0f; if (sensorPlatform == KinectInterop.DepthSensorPlatform.RealSense) { // RS toPointX = extr.rotation[0] * point.x + extr.rotation[3] * point.y + extr.rotation[6] * point.z + extr.translation[0]; toPointY = extr.rotation[1] * point.x + extr.rotation[4] * point.y + extr.rotation[7] * point.z + extr.translation[1]; toPointZ = extr.rotation[2] * point.x + extr.rotation[5] * point.y + extr.rotation[8] * point.z + extr.translation[2]; } else { // K4A, K2 toPointX = extr.rotation[0] * point.x + extr.rotation[1] * point.y + extr.rotation[2] * point.z + extr.translation[0]; toPointY = extr.rotation[3] * point.x + extr.rotation[4] * point.y + extr.rotation[5] * point.z + extr.translation[1]; toPointZ = extr.rotation[6] * point.x + extr.rotation[7] * point.y + extr.rotation[8] * point.z + extr.translation[2]; } return new Vector3(toPointX, toPointY, toPointZ); } public override Vector3[] GetDepthCameraSpaceTable(KinectInterop.SensorData sensorData) { if (sensorData == null) return null; if (depth2SpaceTable == null || depth2SpaceWidth != sensorData.depthImageWidth || depth2SpaceHeight != sensorData.depthImageHeight) { depth2SpaceWidth = sensorData.depthImageWidth; depth2SpaceHeight = sensorData.depthImageHeight; int depthImageLength = sensorData.depthImageWidth * sensorData.depthImageHeight; depth2SpaceTable = new Vector3[depthImageLength]; //bNeedDST = true; SendControlMessage(ControlMessageType.GetDST); //bDSTsent = true; } // wait for net-dst long timeStart = System.DateTime.Now.Ticks; long timeNow = timeStart; while (!bGotDST && (timeNow - timeStart) < 50000000) // timeout - 5 seconds { Thread.Sleep(THREAD_WAIT_TIME_MS); timeNow = System.DateTime.Now.Ticks; } if(!bGotDST) { Debug.LogWarning("Timed out waiting for net-dst."); } return depth2SpaceTable; } public override Vector3[] GetColorCameraSpaceTable(KinectInterop.SensorData sensorData) { if (sensorData == null) return null; if (color2SpaceTable == null || color2SpaceWidth != sensorData.colorImageWidth || color2SpaceHeight != sensorData.colorImageHeight) { color2SpaceWidth = sensorData.colorImageWidth; color2SpaceHeight = sensorData.colorImageHeight; int colorImageLength = sensorData.colorImageWidth * sensorData.colorImageHeight; color2SpaceTable = new Vector3[colorImageLength]; //bNeedCST = true; SendControlMessage(ControlMessageType.GetCST); //bCSTsent = true; } // wait for net-cst long timeStart = System.DateTime.Now.Ticks; long timeNow = timeStart; while (!bGotCST && (timeNow - timeStart) < 50000000) // timeout - 5 seconds { Thread.Sleep(THREAD_WAIT_TIME_MS); timeNow = System.DateTime.Now.Ticks; } if (!bGotCST) { Debug.LogWarning("Timed out waiting for net-cst."); } return color2SpaceTable; } // returns the point cloud texture resolution public override Vector2Int GetPointCloudTexResolution(KinectInterop.SensorData sensorData) { Vector2Int texRes = Vector2Int.zero; // wait for net-cst long timeStart = System.DateTime.Now.Ticks; long timeNow = timeStart; while (texRes == Vector2Int.zero && (timeNow - timeStart) < 50000000) // timeout - 5 seconds { switch (pointCloudResolution) { case PointCloudResolution.DepthCameraResolution: texRes = new Vector2Int(sensorData.depthImageWidth, sensorData.depthImageHeight); break; case PointCloudResolution.ColorCameraResolution: texRes = new Vector2Int(sensorData.colorImageWidth, sensorData.colorImageHeight); break; } if(texRes == Vector2Int.zero) { Thread.Sleep(THREAD_WAIT_TIME_MS); } timeNow = System.DateTime.Now.Ticks; } if (texRes == Vector2Int.zero) { throw new System.Exception("Unsupported point cloud resolution: " + pointCloudResolution + " or the respective image is not available."); } return texRes; } // creates the point-cloud vertex shader and its respective buffers, as needed protected override bool CreatePointCloudVertexShader(KinectInterop.SensorData sensorData) { bool bSuccess = base.CreatePointCloudVertexShader(sensorData); if (pointCloudResolution == PointCloudResolution.ColorCameraResolution && color2depthFrameClient == null) { color2depthFrameDecompressor = LZ4DecompressorFactory.CreateNew(); color2depthFrameClient = new TcpNetClient(sbConsole, color2depthFrameDecompressor); color2depthFrameClient.ConnectToServer(serverHost, serverBasePort + (int)NetMessageType.Color2Depth, "tdepth", null); color2depthFrameClient.ReceivedMessage += new ReceivedMessageEventHandler(Color2DepthFrameReceived); } return bSuccess; } // disposes the point-cloud vertex shader and its respective buffers protected override void DisposePointCloudVertexShader(KinectInterop.SensorData sensorData) { base.DisposePointCloudVertexShader(sensorData); if(color2depthFrameClient != null) { color2depthFrameClient.ReceivedMessage -= Color2DepthFrameReceived; color2depthFrameClient.Close(); color2depthFrameClient = null; } } // creates the point-cloud color shader and its respective buffers, as needed protected override bool CreatePointCloudColorShader(KinectInterop.SensorData sensorData) { if (pointCloudResolution == PointCloudResolution.DepthCameraResolution) { if (pointCloudAlignedColorTex == null) { pointCloudAlignedColorTex = new Texture2D(sensorData.depthImageWidth, sensorData.depthImageHeight, sensorData.colorImageFormat, false); pointCloudAlignedColorTex.wrapMode = TextureWrapMode.Clamp; pointCloudAlignedColorTex.filterMode = FilterMode.Point; } if (depth2colorFrameClient == null) { depth2colorFrameClient = new TcpNetClient(sbConsole, null); depth2colorFrameClient.ConnectToServer(serverHost, serverBasePort + (int)NetMessageType.Depth2Color, "tcolor", null); depth2colorFrameClient.ReceivedMessage += new ReceivedMessageEventHandler(Depth2ColorFrameReceived); } return true; } else { return base.CreatePointCloudColorShader(sensorData); } } // disposes the point-cloud color shader and its respective buffers protected override void DisposePointCloudColorShader(KinectInterop.SensorData sensorData) { if(depth2colorFrameClient != null) { depth2colorFrameClient.ReceivedMessage -= Depth2ColorFrameReceived; depth2colorFrameClient.Close(); depth2colorFrameClient = null; pointCloudAlignedColorTex = null; } else { base.DisposePointCloudColorShader(sensorData); } } // updates the point-cloud color shader with the actual data protected override bool UpdatePointCloudColorShader(KinectInterop.SensorData sensorData) { if (depth2colorFrameClient != null) { // depth2color frame if (depth2colorImageData != null && sensorData.lastDepthCamColorFrameTime != lastDepth2ColorImageTime) { if (depth2colorImageWidth > 0 && depth2colorImageHeight > 0) { if (pointCloudAlignedColorTex == null || pointCloudAlignedColorTex.width != depth2colorImageWidth || pointCloudAlignedColorTex.height != depth2colorImageHeight) { if (pointCloudAlignedColorTex != null) Destroy(pointCloudAlignedColorTex); pointCloudAlignedColorTex = new Texture2D(depth2colorImageWidth, depth2colorImageHeight, TextureFormat.RGB24, false); pointCloudAlignedColorTex.wrapMode = TextureWrapMode.Clamp; pointCloudAlignedColorTex.filterMode = FilterMode.Point; } } lock (depthCamColorFrameLock) { if (pointCloudAlignedColorTex != null || sensorData.depthCamColorImageTexture != null) { ((Texture2D)pointCloudAlignedColorTex).LoadImage(depth2colorImageData); ((Texture2D)pointCloudAlignedColorTex).Apply(); if (sensorData.depthCamColorImageTexture != null) { Graphics.CopyTexture(pointCloudAlignedColorTex, sensorData.depthCamColorImageTexture); } if(pointCloudColorTexture != null) { Graphics.Blit(pointCloudAlignedColorTex, pointCloudColorTexture); } } sensorData.lastDepthCamColorFrameTime = lastDepthCamColorFrameTime = lastDepth2ColorImageTime; //Debug.Log("UpdateDepth2ColorImageTimestamp: " + lastDepth2ColorImageTime); } if (pointCloudColorTexture != null && pointCloudColorRT != null) { Graphics.CopyTexture(pointCloudColorTexture, pointCloudColorRT); } } return true; } else { return base.UpdatePointCloudColorShader(sensorData); } } // creates the color-cam depth shader and its respective buffers, as needed protected override bool CreateColorDepthShader(KinectInterop.SensorData sensorData) { bool bSuccess = base.CreateColorDepthShader(sensorData); if (color2depthFrameClient == null) { color2depthFrameDecompressor = LZ4DecompressorFactory.CreateNew(); color2depthFrameClient = new TcpNetClient(sbConsole, color2depthFrameDecompressor); color2depthFrameClient.ConnectToServer(serverHost, serverBasePort + (int)NetMessageType.Color2Depth, "tdepth", null); color2depthFrameClient.ReceivedMessage += new ReceivedMessageEventHandler(Color2DepthFrameReceived); } return bSuccess; } // disposes the color-cam depth shader and its respective buffers protected override void DisposeColorDepthShader(KinectInterop.SensorData sensorData) { base.DisposeColorDepthShader(sensorData); if (color2depthFrameClient != null) { color2depthFrameClient.ReceivedMessage -= Color2DepthFrameReceived; color2depthFrameClient.Close(); color2depthFrameClient = null; } } // creates the color-cam infrared shader and its respective buffers, as needed protected override bool CreateColorInfraredShader(KinectInterop.SensorData sensorData) { //bool bSuccess = base.CreateColorInfraredShader(sensorData); if (color2infraredFrameClient == null) { color2infraredFrameClient = new TcpNetClient(sbConsole, null); color2infraredFrameClient.ConnectToServer(serverHost, serverBasePort + (int)NetMessageType.Color2Infrared, "tinfrared", null); color2infraredFrameClient.ReceivedMessage += new ReceivedMessageEventHandler(Color2InfraredFrameReceived); } return true; // bSuccess; } // disposes the color-cam infrared shader and its respective buffers protected override void DisposeColorInfraredShader(KinectInterop.SensorData sensorData) { base.DisposeColorInfraredShader(sensorData); if (color2infraredFrameClient != null) { color2infraredFrameClient.ReceivedMessage -= Color2InfraredFrameReceived; color2infraredFrameClient.Close(); color2infraredFrameClient = null; } } // updates the color-cam infrared shader with the actual data protected override bool UpdateColorInfraredShader(KinectInterop.SensorData sensorData) { if (color2infraredFrameClient != null) { // color2infrared frame if (color2infraredImageData != null && sensorData.lastColorCamInfraredFrameTime != lastColor2InfraredImageTime) { if (color2infraredImageWidth > 0 && color2infraredImageHeight > 0) { if (sensorData.colorInfraredTexture == null || sensorData.colorInfraredTexture.width != color2infraredImageWidth || sensorData.colorInfraredTexture.height != color2infraredImageHeight) { sensorData.colorInfraredTexture = KinectInterop.CreateRenderTexture(sensorData.colorInfraredTexture, color2infraredImageWidth, color2infraredImageHeight, RenderTextureFormat.ARGB32); //sensorData.colorInfraredTexture.enableRandomWrite = true; sensorData.colorInfraredTexture.Create(); } if (colorInfraredTexture == null || colorInfraredTexture.width != color2infraredImageWidth || colorInfraredTexture.height != color2infraredImageHeight) { if (colorInfraredTexture != null) Destroy(colorInfraredTexture); colorInfraredTexture = new Texture2D(color2infraredImageWidth, color2infraredImageHeight, TextureFormat.RGB24, false); colorInfraredTexture.wrapMode = TextureWrapMode.Clamp; colorInfraredTexture.filterMode = FilterMode.Point; } } lock (colorCamInfraredFrameLock) { if (colorInfraredTexture != null || sensorData.colorInfraredTexture != null) { colorInfraredTexture.LoadImage(color2infraredImageData); colorInfraredTexture.Apply(); if (sensorData.colorInfraredTexture != null) { Graphics.CopyTexture(colorInfraredTexture, sensorData.colorInfraredTexture); } } sensorData.lastColorCamInfraredFrameTime = lastColorCamInfraredFrameTime = lastColor2InfraredImageTime; //Debug.Log("UpdateColor2InfraredImageTimestamp: " + lastColor2InfraredImageTime); } } return true; } else { return base.UpdateColorInfraredShader(sensorData); } } // creates the color-cam body-index shader and its respective buffers, as needed protected override bool CreateColorBodyIndexShader(KinectInterop.SensorData sensorData) { bool bSuccess = base.CreateColorBodyIndexShader(sensorData); if (color2bodyIndexFrameClient == null) { color2bodyIndexFrameDecompressor = LZ4DecompressorFactory.CreateNew(); color2bodyIndexFrameClient = new TcpNetClient(sbConsole, color2bodyIndexFrameDecompressor); color2bodyIndexFrameClient.ConnectToServer(serverHost, serverBasePort + (int)NetMessageType.Color2BodyIndex, "tbodyindex", null); color2bodyIndexFrameClient.ReceivedMessage += new ReceivedMessageEventHandler(Color2BodyIndexFrameReceived); } return bSuccess; } // disposes the color-cam body-index shader and its respective buffers protected override void DisposeColorBodyIndexShader(KinectInterop.SensorData sensorData) { base.DisposeColorBodyIndexShader(sensorData); if (color2bodyIndexFrameClient != null) { color2bodyIndexFrameClient.ReceivedMessage -= Color2BodyIndexFrameReceived; color2bodyIndexFrameClient.Close(); color2bodyIndexFrameClient = null; } } // broadcasts to discover the 1st available net-server private void BroadcastServerDiscovery() { UdpClient udpClient = null; if (bBroadcastResponseReceived) return; try { //Debug.Log("Auto discovery - looking for net server..."); udpClient = new UdpClient(); IPEndPoint ep = new IPEndPoint(IPAddress.Any, 0); string sRequestData = "KNS"; byte[] btRequestData = System.Text.Encoding.UTF8.GetBytes(sRequestData); udpClient.EnableBroadcast = true; udpClient.Send(btRequestData, btRequestData.Length, new IPEndPoint(IPAddress.Broadcast, serverBasePort)); UdpState state = new UdpState(); state.ep = ep; state.uc = udpClient; udpClient.BeginReceive(new System.AsyncCallback(BroadcastServerResponseReceived), state); // wait for response // wait for net-cst long timeStart = System.DateTime.Now.Ticks; long timeNow = timeStart; while (!bBroadcastResponseReceived && (timeNow - timeStart) < 50000000) // timeout - 5 seconds { Thread.Sleep(THREAD_WAIT_TIME_MS); timeNow = System.DateTime.Now.Ticks; } if(bBroadcastResponseReceived) { if (consoleLogMessages) Debug.Log("Auto discovery - server host: " + serverHost + ", port: " + serverBasePort); } else { Debug.LogWarning("Timed out waiting for response from the broadcast server."); } } catch (System.Exception ex) { Debug.LogException(ex); } finally { if (udpClient != null) { udpClient.Close(); } } } // invoked when a response from the broadcast server gets received private void BroadcastServerResponseReceived(System.IAsyncResult ar) { UdpState state = (UdpState)(ar.AsyncState); UdpClient uc = state.uc; IPEndPoint ep = state.ep; byte[] btReceiveData = uc.EndReceive(ar, ref ep); string sReceiveData = System.Text.Encoding.UTF8.GetString(btReceiveData); //Debug.Log("Received response from the broadcast server."); if(!string.IsNullOrEmpty(sReceiveData)) { string[] asParts = sReceiveData.Split("|".ToCharArray()); if(asParts.Length >= 3 && asParts[0] == "KNS") { serverHost = asParts[1]; int.TryParse(asParts[2], out serverBasePort); bBroadcastResponseReceived = true; } } } // initializes the network clients private void InitNetClients(KinectInterop.FrameSource dwFlags) { try { if (autoServerDiscovery && !bBroadcastResponseReceived) { // discover the 1st available net-server BroadcastServerDiscovery(); } // clear params //bSensorDataMsgSent = false; bGotNetSensorData = false; bSetNetSensorData = false; bInvalidSensorData = false; lastKeepAliveFrameTime = 0; latestDataReceivedAt = 0; disconnectedAt = 0; lastControlFrameTime = 0; lastColorFrameTime = 0; lastDepthFrameTime = 0; lastInfraredFrameTime = 0; lastBodyDataFrameTime = 0; lastBodyIndexFrameTime = 0; lastPoseFrameTime = 0; lastDepth2colorFrameTime = 0; lastColor2depthFrameTime = 0; lastColor2bodyIndexFrameTime = 0; // init clients controlFrameDecompressor = LZ4DecompressorFactory.CreateNew(); controlFrameClient = new TcpNetClient(sbConsole, controlFrameDecompressor); NetMessageData msgGetData = GetControlMessage(ControlMessageType.GetSensorData); controlFrameClient.ConnectToServer(serverHost, serverBasePort + (int)NetMessageType.Control, "control", msgGetData); controlFrameClient.ReceivedMessage += new ReceivedMessageEventHandler(ControlFrameReceived); if ((dwFlags & KinectInterop.FrameSource.TypeColor) != 0 && getColorFrames) { colorFrameClient = new TcpNetClient(sbConsole, null); colorFrameClient.ConnectToServer(serverHost, serverBasePort + (int)NetMessageType.Color, "color", null); colorFrameClient.ReceivedMessage += new ReceivedMessageEventHandler(ColorFrameReceived); } if ((dwFlags & KinectInterop.FrameSource.TypeDepth) != 0 && getDepthFrames) { depthFrameDecompressor = LZ4DecompressorFactory.CreateNew(); depthFrameClient = new TcpNetClient(sbConsole, depthFrameDecompressor); depthFrameClient.ConnectToServer(serverHost, serverBasePort + (int)NetMessageType.Depth, "depth", null); depthFrameClient.ReceivedMessage += new ReceivedMessageEventHandler(DepthFrameReceived); } if ((dwFlags & KinectInterop.FrameSource.TypeInfrared) != 0 && getInfraredFrames) { infraredFrameClient = new TcpNetClient(sbConsole, null); infraredFrameClient.ConnectToServer(serverHost, serverBasePort + (int)NetMessageType.Infrared, "infrared", null); infraredFrameClient.ReceivedMessage += new ReceivedMessageEventHandler(InfraredFrameReceived); } if ((dwFlags & KinectInterop.FrameSource.TypeBody) != 0 && getBodyFrames) { bodyDataFrameClient = new TcpNetClient(sbConsole, null); bodyDataFrameClient.ConnectToServer(serverHost, serverBasePort + (int)NetMessageType.BodyData, "body-data", null); bodyDataFrameClient.ReceivedMessage += new ReceivedMessageEventHandler(BodyDataFrameReceived); } if ((dwFlags & KinectInterop.FrameSource.TypeBodyIndex) != 0 && getBodyIndexFrames) { bodyIndexFrameDecompressor = LZ4DecompressorFactory.CreateNew(); bodyIndexFrameClient = new TcpNetClient(sbConsole, bodyIndexFrameDecompressor); bodyIndexFrameClient.ConnectToServer(serverHost, serverBasePort + (int)NetMessageType.BodyIndex, "body-index", null); bodyIndexFrameClient.ReceivedMessage += new ReceivedMessageEventHandler(BodyIndexFrameReceived); } if ((dwFlags & KinectInterop.FrameSource.TypePose) != 0 && getPoseFrames) { poseFrameClient = new TcpNetClient(sbConsole, null); poseFrameClient.ConnectToServer(serverHost, serverBasePort + (int)NetMessageType.Pose, "pose", null); poseFrameClient.ReceivedMessage += new ReceivedMessageEventHandler(PoseFrameReceived); } } catch (System.Exception ex) { Debug.LogError("Error while initing the net clients."); Debug.LogException(ex); } } // closes all network clients private void CloseNetClients() { try { // dispose coord mapping shaders DisposePointCloudVertexShader(sensorData); DisposePointCloudColorShader(sensorData); DisposeColorDepthShader(sensorData); DisposeColorInfraredShader(sensorData); DisposeColorBodyIndexShader(sensorData); if (controlFrameClient != null) { SendControlMessage(ControlMessageType.Disconnect); controlFrameClient.ReceivedMessage -= ControlFrameReceived; controlFrameClient.Close(); controlFrameClient = null; controlFrameDecompressor = null; } if (colorFrameClient != null) { colorFrameClient.ReceivedMessage -= ColorFrameReceived; colorFrameClient.Close(); colorFrameClient = null; } if (depthFrameClient != null) { depthFrameClient.ReceivedMessage -= DepthFrameReceived; depthFrameClient.Close(); depthFrameClient = null; depthFrameDecompressor = null; } if (infraredFrameClient != null) { infraredFrameClient.ReceivedMessage -= InfraredFrameReceived; infraredFrameClient.Close(); infraredFrameClient = null; } if (bodyDataFrameClient != null) { bodyDataFrameClient.ReceivedMessage -= BodyDataFrameReceived; bodyDataFrameClient.Close(); bodyDataFrameClient = null; } if (bodyIndexFrameClient != null) { bodyIndexFrameClient.ReceivedMessage -= BodyIndexFrameReceived; bodyIndexFrameClient.Close(); bodyIndexFrameClient = null; bodyIndexFrameDecompressor = null; } if (poseFrameClient != null) { poseFrameClient.ReceivedMessage -= PoseFrameReceived; poseFrameClient.Close(); poseFrameClient = null; } if (depth2colorFrameClient != null) { depth2colorFrameClient.ReceivedMessage -= Depth2ColorFrameReceived; depth2colorFrameClient.Close(); depth2colorFrameClient = null; } if (color2depthFrameClient != null) { color2depthFrameClient.ReceivedMessage -= Color2DepthFrameReceived; color2depthFrameClient.Close(); color2depthFrameClient = null; color2depthFrameDecompressor = null; } if (color2bodyIndexFrameClient != null) { color2bodyIndexFrameClient.ReceivedMessage -= Color2BodyIndexFrameReceived; color2bodyIndexFrameClient.Close(); color2bodyIndexFrameClient = null; color2bodyIndexFrameDecompressor = null; } } catch (System.Exception ex) { Debug.LogError("Error while closing the net servers."); Debug.LogException(ex); } } private void ControlFrameReceived(object state, ReceivedMessageEventArgs args) { if (sensorData == null) return; try { // ignore keep-alives from the server if(args.message.frameData.Length > 2) { switch(args.message.encType) { case FrameEncodeType.SensorDataJson: // get-sensor-data CtrlGotNetSensorData(args.message.frameData); break; case FrameEncodeType.DSTraw: // get-dst CtrlGotNetDST(args.message.frameData); break; case FrameEncodeType.CSTraw: // get-cst CtrlGotNetCST(args.message.frameData); break; } } lastControlFrameTime = (ulong)System.DateTime.Now.Ticks; } catch (System.Exception ex) { Debug.LogException(ex); } } // process net-sensor-data bytes private void CtrlGotNetSensorData(byte[] btData) { string sNetSensorData = System.Text.Encoding.UTF8.GetString(btData); //Debug.Log("NetSensorData (" + btData.Length + " bytes): " + sNetSensorData); netSensorData = JsonUtility.FromJson(sNetSensorData); //System.IO.File.WriteAllText("NetSensorData.json", sNetSensorData); bGotNetSensorData = true; //lastControlFrameTime = args.message.timestamp; if (consoleLogMessages) Debug.Log("Got net-sensor-data"); } // process net-dst bytes private void CtrlGotNetDST(byte[] btData) { if (depth2SpaceTable == null) { int depthImageLength = sensorData.depthImageWidth * sensorData.depthImageHeight; depth2SpaceTable = new Vector3[depthImageLength]; } KinectInterop.CopyBytes(btData, 1, depth2SpaceTable, 3 * sizeof(float)); //System.IO.File.WriteAllBytes("NetDST.bin", btData); //lastControlFrameTime = args.message.timestamp; bGotDST = true; if (consoleLogMessages) Debug.Log("Got net-dst"); } // process net-cst bytes private void CtrlGotNetCST(byte[] btData) { if (color2SpaceTable == null) { int colorImageLength = sensorData.colorImageWidth * sensorData.colorImageHeight; color2SpaceTable = new Vector3[colorImageLength]; } KinectInterop.CopyBytes(btData, 1, color2SpaceTable, 3 * sizeof(float)); //System.IO.File.WriteAllBytes("NetCST.bin", btData); //lastControlFrameTime = args.message.timestamp; bGotCST = true; if (consoleLogMessages) Debug.Log("Got net-cst"); } private void ColorFrameReceived(object state, ReceivedMessageEventArgs args) { if (sensorData == null) return; try { if (sensorData.colorImageWidth == 0) { sensorData.colorImageWidth = args.message.frameWidth; sensorData.colorImageHeight = args.message.frameHeight; } if (isSyncDepthAndColor && !args.synched) { SyncReceivedFrame(args.message); return; } lock (colorFrameLock) { colorImageData = args.message.frameData; lastColorImageTime = args.message.timestamp; //Debug.Log("ReceivedColorTimestamp: " + lastColorImageTime); lastColorFrameTime = (ulong)System.DateTime.Now.Ticks; } } catch (System.Exception ex) { Debug.LogException(ex); } } private void DepthFrameReceived(object state, ReceivedMessageEventArgs args) { if (sensorData == null) return; try { if (sensorData.depthImage == null) { sensorData.depthImageWidth = args.message.frameWidth; sensorData.depthImageHeight = args.message.frameHeight; rawDepthImage = new ushort[sensorData.depthImageWidth * sensorData.depthImageHeight]; sensorData.depthImage = new ushort[sensorData.depthImageWidth * sensorData.depthImageHeight]; } if(isFrameSyncNeeded && !args.synched) { SyncReceivedFrame(args.message); return; } lock (depthFrameLock) { KinectInterop.CopyBytes(args.message.frameData, sizeof(byte), rawDepthImage, sizeof(ushort)); rawDepthTimestamp = args.message.timestamp; //Debug.Log("ReceivedDepthTimestamp: " + rawDepthTimestamp); lastDepthFrameTime = (ulong)System.DateTime.Now.Ticks; } } catch (System.Exception ex) { Debug.LogException(ex); } } private void InfraredFrameReceived(object state, ReceivedMessageEventArgs args) { if (sensorData == null) return; try { if (infraredImageWidth == 0 || infraredImageHeight == 0) { infraredImageWidth = args.message.frameWidth; infraredImageHeight = args.message.frameHeight; } if (isFrameSyncNeeded && !args.synched) { SyncReceivedFrame(args.message); return; } lock (infraredFrameLock) { infraredImageData = args.message.frameData; lastInfraredImageTime = args.message.timestamp; //Debug.Log("ReceivedInfraredTimestamp: " + lastInfraredImageTime); lastInfraredFrameTime = (ulong)System.DateTime.Now.Ticks; } } catch (System.Exception ex) { Debug.LogException(ex); } } private void BodyDataFrameReceived(object state, ReceivedMessageEventArgs args) { if (sensorData == null) return; try { if (sensorData.alTrackedBodies == null) { sensorData.alTrackedBodies = new KinectInterop.BodyData[0]; sensorData.trackedBodiesCount = 0; } if (isSyncBodyAndDepth && !args.synched) { SyncReceivedFrame(args.message); return; } lock (bodyTrackerLock) { Matrix4x4 sensorToWorld = GetSensorToWorldMatrix(); string sBodyFrameData = System.Text.Encoding.UTF8.GetString(args.message.frameData); sensorData.trackedBodiesCount = KinectInterop.SetBodyFrameFromCsv(sBodyFrameData, "\t", sensorData, ref sensorData.alTrackedBodies, ref sensorToWorld, bIgnoreZcoords, out rawBodyTimestamp); sensorData.lastBodyFrameTime = currentBodyTimestamp = rawBodyTimestamp = args.message.timestamp; //Debug.Log("ReceivedBodyTimestamp: " + rawBodyTimestamp); lastBodyDataFrameTime = (ulong)System.DateTime.Now.Ticks; } } catch (System.Exception ex) { Debug.LogException(ex); } } private void BodyIndexFrameReceived(object state, ReceivedMessageEventArgs args) { if (sensorData == null) return; try { if (sensorData.bodyIndexImage == null) { //rawBodyIndexImage = new byte[args.message.frameWidth * args.message.frameHeight]; sensorData.bodyIndexImage = new byte[args.message.frameWidth * args.message.frameHeight]; } if (isSyncBodyAndDepth && !args.synched) { SyncReceivedFrame(args.message); return; } lock (bodyTrackerLock) { rawBodyIndexImage = args.message.frameData; rawBodyIndexTimestamp = args.message.timestamp; //Debug.Log("ReceivedBodyIndexTimestamp: " + rawBodyIndexTimestamp); lastBodyIndexFrameTime = (ulong)System.DateTime.Now.Ticks; } } catch (System.Exception ex) { Debug.LogException(ex); } } private void PoseFrameReceived(object state, ReceivedMessageEventArgs args) { if (sensorData == null) return; try { lock(netPoseLock) { string sNetPoseData = System.Text.Encoding.UTF8.GetString(args.message.frameData); netPoseData = JsonUtility.FromJson(sNetPoseData); lastPoseFrameTime = (ulong)System.DateTime.Now.Ticks; } } catch (System.Exception ex) { Debug.LogException(ex); } } private void Depth2ColorFrameReceived(object state, ReceivedMessageEventArgs args) { if (sensorData == null) return; try { if (depth2colorImageWidth == 0 || depth2colorImageHeight == 0) { depth2colorImageWidth = args.message.frameWidth; depth2colorImageHeight = args.message.frameHeight; } if (isSyncDepthAndColor && !args.synched) { SyncReceivedFrame(args.message); return; } lock (depthCamColorFrameLock) { depth2colorImageData = args.message.frameData; lastDepth2ColorImageTime = args.message.timestamp; //Debug.Log("ReceivedDepthCamColorTimestamp: " + lastDepth2ColorImageTime); lastDepth2colorFrameTime = (ulong)System.DateTime.Now.Ticks; } } catch (System.Exception ex) { Debug.LogException(ex); } } private void Color2DepthFrameReceived(object state, ReceivedMessageEventArgs args) { if (sensorData == null) return; try { if (colorCamDepthDataFrame == null || (args.message.frameWidth * args.message.frameHeight) != colorCamDepthDataFrame.Length) { colorCamDepthDataFrame = new ushort[args.message.frameWidth * args.message.frameHeight]; } if (isSyncDepthAndColor && !args.synched) { SyncReceivedFrame(args.message); return; } lock (colorCamDepthFrameLock) { KinectInterop.CopyBytes(args.message.frameData, sizeof(byte), colorCamDepthDataFrame, sizeof(ushort)); lastColorCamDepthFrameTime = args.message.timestamp; //Debug.Log("ReceivedColorCamDepthTimestamp: " + lastColorCamDepthFrameTime); lastColor2depthFrameTime = (ulong)System.DateTime.Now.Ticks; } } catch (System.Exception ex) { Debug.LogException(ex); } } private void Color2InfraredFrameReceived(object state, ReceivedMessageEventArgs args) { if (sensorData == null) return; try { if (color2infraredImageWidth == 0 || color2infraredImageHeight == 0) { color2infraredImageWidth = args.message.frameWidth; color2infraredImageHeight = args.message.frameHeight; } if (isSyncDepthAndColor && !args.synched) { SyncReceivedFrame(args.message); return; } lock (colorCamInfraredFrameLock) { color2infraredImageData = args.message.frameData; lastColor2InfraredImageTime = args.message.timestamp; //Debug.Log("ReceivedColorCamInfraredTimestamp: " + lastColor2InfraredImageTime); lastColor2infraredFrameTime = (ulong)System.DateTime.Now.Ticks; } } catch (System.Exception ex) { Debug.LogException(ex); } } private void Color2BodyIndexFrameReceived(object state, ReceivedMessageEventArgs args) { if (sensorData == null) return; try { if (colorCamBodyIndexFrame == null || (args.message.frameWidth * args.message.frameHeight) != colorCamBodyIndexFrame.Length) { colorCamBodyIndexFrame = new byte[args.message.frameWidth * args.message.frameHeight]; } if (isSyncBodyAndDepth && !args.synched) { SyncReceivedFrame(args.message); return; } lock (colorCamBodyIndexFrameLock) { KinectInterop.CopyBytes(args.message.frameData, sizeof(byte), colorCamBodyIndexFrame, sizeof(byte)); lastColorCamBodyIndexFrameTime = args.message.timestamp; //Debug.Log("ReceivedColorCamBodyIndexTimestamp: " + lastColorCamBodyIndexFrameTime); lastColor2bodyIndexFrameTime = (ulong)System.DateTime.Now.Ticks; } } catch (System.Exception ex) { Debug.LogException(ex); } } // synchronizes the received frame with other frames at the same time private void SyncReceivedFrame(NetMessageData message) { if (dictNetMessageData == null || alNetMessageTime == null) return; lock (syncMessageLock) { ulong frameTime = message.timestamp / 10; if (dictNetMessageData.ContainsKey(frameTime)) { // update the timestamp entry with this type of message Dictionary dictTimeFrames = dictNetMessageData[frameTime]; dictTimeFrames[message.msgType] = message; dictNetMessageData[frameTime] = dictTimeFrames; //Debug.Log(" updated " + frameTime + " - " + message.msgType + " frame, frame-count: " + dictNetMessageData[frameTime].Count + ", missing: " + GetMissingSyncFramesInfo(frameTime, dictTimeFrames)); SyncCheckIfAllFramesReady(frameTime, dictTimeFrames); } else { // check for timeout of the oldest messages CheckSyncMessagesTimeout(); // check if the message is before the oldest one in the list ulong minTime = alNetMessageTime.Count > 0 ? alNetMessageTime[0] : 0; if(frameTime >= minTime) { // create 1st entry for timestamp Dictionary dictTimeFrames = new Dictionary(); dictTimeFrames[message.msgType] = message; dictNetMessageData[frameTime] = dictTimeFrames; alNetMessageTime.Add(frameTime); //Debug.Log("Added " + frameTime + " - " + message.msgType + " frame, cache-size: " + alNetMessageTime.Count + ", missing: " + GetMissingSyncFramesInfo(frameTime, dictTimeFrames)); if (alNetMessageTime.Count > 1) { alNetMessageTime.Sort(); } while (alNetMessageTime.Count > 20) { ulong msgTime = alNetMessageTime[0]; int msgDataCount = 0; string missingFrames = string.Empty; if (dictNetMessageData.ContainsKey(msgTime)) { msgDataCount = dictNetMessageData[msgTime].Count; missingFrames = GetMissingSyncFramesInfo(msgTime, dictNetMessageData[msgTime]); dictNetMessageData[msgTime].Clear(); dictNetMessageData.Remove(msgTime); } alNetMessageTime.RemoveAt(0); if (consoleLogMessages) Debug.LogWarning("Removed entry: " + msgTime + ", frame-count: " + msgDataCount + ", cache-size: " + alNetMessageTime.Count + ", missing: " + missingFrames); } } else { //Debug.Log(" skipping " + frameTime + " - " + message.msgType + " frame < " + minTime); } } } } // checks for timed out messages in the waiting list (> 5 seconds) and removes them, if found private void CheckSyncMessagesTimeout() { long timeNow = System.DateTime.UtcNow.Ticks; const long MESSAGE_TIMEOUT = 50000000; // 5 seconds for (int i = alNetMessageTime.Count - 1; i >= 0; i--) { ulong msgTime = alNetMessageTime[i]; if (dictNetMessageData.ContainsKey(msgTime)) { var dictTimeFrames = dictNetMessageData[msgTime]; var listMessages = dictTimeFrames.Values.ToArray(); foreach (NetMessageData message in listMessages) { if ((timeNow - message.timeCreated) > MESSAGE_TIMEOUT) { //Debug.Log(" removing timed-out message " + msgTime + "-" + message.msgType + ", duration: " + (timeNow - message.timeCreated)); dictNetMessageData[msgTime].Remove(message.msgType); } } } if (!dictNetMessageData.ContainsKey(msgTime) || dictNetMessageData[msgTime].Count == 0) { if(dictNetMessageData.ContainsKey(msgTime)) dictNetMessageData.Remove(msgTime); alNetMessageTime.RemoveAt(i); if (consoleLogMessages) Debug.LogWarning("Removed timed-out entry: " + msgTime + ", cache-size: " + alNetMessageTime.Count); } } } // returns the list of missing frames as string private string GetMissingSyncFramesInfo(ulong frameTime, Dictionary dictTimeFrames) { System.Text.StringBuilder sbBuf = new System.Text.StringBuilder(); if (isSyncDepthAndColor && colorFrameClient != null && !dictTimeFrames.ContainsKey(NetMessageType.Color)) sbBuf.Append("color "); if (isFrameSyncNeeded && depthFrameClient != null && !dictTimeFrames.ContainsKey(NetMessageType.Depth)) sbBuf.Append("depth "); if (isFrameSyncNeeded && infraredFrameClient != null && !dictTimeFrames.ContainsKey(NetMessageType.Infrared)) sbBuf.Append("ir "); if (isSyncBodyAndDepth && bodyDataFrameClient != null && !dictTimeFrames.ContainsKey(NetMessageType.BodyData)) sbBuf.Append("body "); if (isSyncBodyAndDepth && bodyIndexFrameClient != null && !dictTimeFrames.ContainsKey(NetMessageType.BodyIndex)) sbBuf.Append("bi "); if (isSyncDepthAndColor && depth2colorFrameClient != null && !dictTimeFrames.ContainsKey(NetMessageType.Depth2Color)) sbBuf.Append("d2c "); if (isSyncDepthAndColor && color2depthFrameClient != null && !dictTimeFrames.ContainsKey(NetMessageType.Color2Depth)) sbBuf.Append("c2d "); if (isSyncDepthAndColor && color2infraredFrameClient != null && !dictTimeFrames.ContainsKey(NetMessageType.Color2Infrared)) sbBuf.Append("c2i "); if (isSyncBodyAndDepth && color2bodyIndexFrameClient != null && !dictTimeFrames.ContainsKey(NetMessageType.Color2BodyIndex)) sbBuf.Append("c2bi "); if(sbBuf.Length == 0) { sbBuf.Append(""); } return sbBuf.ToString(); } // check if all needed frames are ready private void SyncCheckIfAllFramesReady(ulong frameTime, Dictionary dictTimeFrames) { bool bAllFramesPresent = true; if (isSyncDepthAndColor && colorFrameClient != null) bAllFramesPresent &= dictTimeFrames.ContainsKey(NetMessageType.Color); if (isFrameSyncNeeded && depthFrameClient != null) bAllFramesPresent &= dictTimeFrames.ContainsKey(NetMessageType.Depth); if (isFrameSyncNeeded && infraredFrameClient != null) bAllFramesPresent &= dictTimeFrames.ContainsKey(NetMessageType.Infrared); if (isSyncBodyAndDepth && bodyDataFrameClient != null) bAllFramesPresent &= dictTimeFrames.ContainsKey(NetMessageType.BodyData); if (isSyncBodyAndDepth && bodyIndexFrameClient != null) bAllFramesPresent &= dictTimeFrames.ContainsKey(NetMessageType.BodyIndex); if (isSyncDepthAndColor && depth2colorFrameClient != null) bAllFramesPresent &= dictTimeFrames.ContainsKey(NetMessageType.Depth2Color); if (isSyncDepthAndColor && color2depthFrameClient != null) bAllFramesPresent &= dictTimeFrames.ContainsKey(NetMessageType.Color2Depth); if (isSyncDepthAndColor && color2infraredFrameClient != null) bAllFramesPresent &= dictTimeFrames.ContainsKey(NetMessageType.Color2Infrared); if (isSyncBodyAndDepth && color2bodyIndexFrameClient != null) bAllFramesPresent &= dictTimeFrames.ContainsKey(NetMessageType.Color2BodyIndex); if (bAllFramesPresent) { SyncSendSynchedFrames(frameTime, dictTimeFrames); } } // re-sends the synchronized frames private void SyncSendSynchedFrames(ulong frameTime, Dictionary dictTimeFrames) { //Debug.Log("Resending " + dictTimeFrames.Count + " synched frames at entry: " + frameTime); foreach (NetMessageData message in dictTimeFrames.Values) { ReceivedMessageEventArgs args = new ReceivedMessageEventArgs(message, true); switch (message.msgType) { case NetMessageType.Color: ColorFrameReceived(this, args); break; case NetMessageType.Depth: DepthFrameReceived(this, args); break; case NetMessageType.Infrared: InfraredFrameReceived(this, args); break; case NetMessageType.BodyData: BodyDataFrameReceived(this, args); break; case NetMessageType.BodyIndex: BodyIndexFrameReceived(this, args); break; case NetMessageType.Depth2Color: Depth2ColorFrameReceived(this, args); break; case NetMessageType.Color2Depth: Color2DepthFrameReceived(this, args); break; case NetMessageType.Color2Infrared: Color2InfraredFrameReceived(this, args); break; case NetMessageType.Color2BodyIndex: Color2BodyIndexFrameReceived(this, args); break; default: Debug.LogError("Unknown message type: " + message.msgType); break; } } // clean up if(alNetMessageTime != null && dictNetMessageData != null) { while (alNetMessageTime.Count > 0) { ulong msgTime = alNetMessageTime[0]; int msgDataCount = 0; if (dictNetMessageData.ContainsKey(msgTime)) { msgDataCount = dictNetMessageData[msgTime].Count; dictNetMessageData[msgTime].Clear(); dictNetMessageData.Remove(msgTime); } alNetMessageTime.RemoveAt(0); //Debug.Log("Synched " + msgTime + ", frame-time: " + frameTime + ", frame-count: " + msgDataCount + ", cache-size: " + alNetMessageTime.Count); if (msgTime == frameTime) break; } } } // cleans up the waiting for sync message data private void CleanupSyncMessageData() { if (alNetMessageTime != null && dictNetMessageData != null) { for (int i = alNetMessageTime.Count - 1; i >= 0; i--) { ulong msgTime = alNetMessageTime[i]; if (dictNetMessageData.ContainsKey(msgTime)) { dictNetMessageData[msgTime].Clear(); dictNetMessageData.Remove(msgTime); } alNetMessageTime.RemoveAt(i); } //if (consoleLogMessages) // Debug.Log("Cleaned up the sync message data."); } } // constructs and returns the control message private NetMessageData GetControlMessage(ControlMessageType messageType) { ulong ulTimeNow = (ulong)System.DateTime.Now.Ticks; NetMessageData msg = new NetMessageData(NetMessageType.Control, FrameEncodeType.Raw, 0, 0, ulTimeNow); byte[] btMsgData = new byte[1]; btMsgData[0] = (byte)messageType; msg.SetData(btMsgData); return msg; } // sends control message to the server private void SendControlMessage(ControlMessageType messageType) { if(controlFrameClient != null && controlFrameClient.IsActive()) { NetMessageData msg = GetControlMessage(messageType); controlFrameClient.SendMessageToAllConnections(msg); //Debug.Log("Sending ctrl message " + messageType); } } // return the latest timestamp private ulong GetLatestTimestamp() { ulong maxFrameTime = lastControlFrameTime; if (lastColorFrameTime != 0 && lastColorFrameTime > maxFrameTime) maxFrameTime = lastColorFrameTime; if (lastDepthFrameTime != 0 && lastDepthFrameTime > maxFrameTime) maxFrameTime = lastDepthFrameTime; if (lastInfraredFrameTime != 0 && lastInfraredFrameTime > maxFrameTime) maxFrameTime = lastInfraredFrameTime; if (lastBodyDataFrameTime != 0 && lastBodyDataFrameTime > maxFrameTime) maxFrameTime = lastBodyDataFrameTime; if (lastBodyIndexFrameTime != 0 && lastBodyIndexFrameTime > maxFrameTime) maxFrameTime = lastBodyIndexFrameTime; if (lastPoseFrameTime != 0 && lastPoseFrameTime > maxFrameTime) maxFrameTime = lastPoseFrameTime; if (lastDepth2colorFrameTime != 0 && lastDepth2colorFrameTime > maxFrameTime) maxFrameTime = lastDepth2colorFrameTime; if (lastColor2depthFrameTime != 0 && lastColor2depthFrameTime > maxFrameTime) maxFrameTime = lastColor2depthFrameTime; if (lastColor2infraredFrameTime != 0 && lastColor2infraredFrameTime > maxFrameTime) maxFrameTime = lastColor2infraredFrameTime; if (lastColor2bodyIndexFrameTime != 0 && lastColor2bodyIndexFrameTime > maxFrameTime) maxFrameTime = lastColor2bodyIndexFrameTime; return maxFrameTime; } } /// /// TcpNetClient provides network client functionality over tcp streams. /// This class is based on the excellent work of Andy Wilson and Hrvoje Benko in "Room Alive Toolkit" - https://github.com/microsoft/RoomAliveToolkit /// internal class TcpNetClient { //private const int bufferSize = 10240000; public int tmpBufferSize = 640000; private System.Text.StringBuilder sbConsole = null; private ILZ4Decompressor decompressor = null; private string streamDesc = string.Empty; private List connections = new List(); private NetMessageData sendMsgOnConnect = null; // thread signal public ManualResetEvent allDoneClient = new ManualResetEvent(false); public ReceivedMessageEventHandler ReceivedMessage = null; public bool IsConnected() { return (connections.Count > 0); } public int GetConnCount() { return connections.Count; } public bool IsActive() { bool bActive = (connections.Count > 0) ? true : false; foreach (NetConnData conn in connections) { if (!conn.isActive) { bActive = false; break; } } return bActive; } public bool IsReadyToSend() { bool bReady = (connections.Count > 0) ? true : false; foreach (NetConnData conn in connections) { if (!conn.readyToSend) { bReady = false; break; } } return bReady; } //public void CloseConnection(int connId) //{ // foreach (NetConnData conn in connections) // { // if (conn.ID == connId) // { // //Debug.Log("Closing connection " + conn.ID + " to " + conn.remoteEP); // LogToConsole("Closing connection to " + streamDesc + " server."); // conn.isActive = false; // connections.Remove(conn); // break; // } // } //} public void CloseConnectionsTo(string remoteIP) { for (int i = connections.Count - 1; i >= 0; i--) { NetConnData conn = connections[i]; if (conn.remoteIP == remoteIP) { //Debug.Log("Closing connection " + conn.ID + " to " + conn.remoteEP); LogToConsole("Closing connection to " + streamDesc + " server."); conn.isActive = false; connections.Remove(conn); } } } public void CloseAllConnections() { foreach (NetConnData conn in connections) { //Debug.Log("Closing connection " + conn.ID + " to " + conn.remoteEP); LogToConsole("Closing connection to " + streamDesc + " server."); conn.isActive = false; } connections.Clear(); } public void SendMessageToAllConnections(NetMessageData message) { foreach (NetConnData conn in connections) { lock (conn.messageQueue) { if (conn.messageQueue.Count < NetConnData.MaxMessageQueueLength) { conn.messageQueue.Enqueue(message); } } } } public void SendMessageToConnection(NetMessageData message, int connId) { foreach (NetConnData conn in connections) { if (conn.ID == connId) { lock (conn.messageQueue) { if (conn.messageQueue.Count < NetConnData.MaxMessageQueueLength) { conn.messageQueue.Enqueue(message); } } break; } } } public TcpNetClient(System.Text.StringBuilder sbConsole, ILZ4Decompressor decompressor) { this.sbConsole = sbConsole; this.decompressor = decompressor; } public void Close() { allDoneClient.Set(); foreach (NetConnData cs in connections) { cs.isActive = false; } } public void ConnectToServer(string host, int port, string streamDesc, NetMessageData msgOnConnect) { this.streamDesc = streamDesc; this.sendMsgOnConnect = msgOnConnect; try { TcpClient client = new TcpClient(AddressFamily.InterNetwork); IPAddress ipAddress = null; bool isIpAddr = host.Length > 0 && host[0] >= '0' && host[0] <= '9'; if (isIpAddr) { ipAddress = IPAddress.Parse(host); } else { IPHostEntry ipHostInfo = Dns.GetHostEntry(host); ipAddress = ipHostInfo.AddressList.Where(a => a.AddressFamily == AddressFamily.InterNetwork).First(); } //Debug.Log("Connecting to " + host + " - " + ipAddress + ":" + port); LogToConsole("Connecting to " + streamDesc + " server at " + ipAddress + ":" + port); client.BeginConnect(ipAddress, port, new System.AsyncCallback(HandleNetServerConnected), client); } catch (System.Exception ex) { //Debug.LogError("Connection to server failed: " + ex.Message); LogErrorToConsole("Connection to " + streamDesc + " server failed: " + ex.Message); //Debug.LogException(ex); } } private void HandleNetServerConnected(System.IAsyncResult ar) { try { NetConnData conn = new NetConnData(); conn.client = (TcpClient)ar.AsyncState; conn.client.EndConnect(ar); conn.stream = conn.client.GetStream(); conn.remoteIP = conn.client.Client.RemoteEndPoint.ToString(); int iP = conn.remoteIP.IndexOf(':'); if (iP >= 0) conn.remoteIP = conn.remoteIP.Substring(0, iP); connections.Add(conn); //Debug.Log("Connected to server " + conn.remoteEP); LogToConsole("Connected to " + streamDesc + " server " + conn.remoteIP); if(sendMsgOnConnect != null) { SendMessageToConnection(sendMsgOnConnect, conn.ID); } NetClientProc(conn); } catch (System.ObjectDisposedException) { // do nothing } catch (System.Exception ex) { //Debug.LogError("Connection to server failed: " + ex.Message); LogErrorToConsole("Connection to " + streamDesc + " server failed: " + ex.Message); //Debug.LogException(ex); } } private void NetClientProc(NetConnData conn) { conn.isActive = true; int bytesReceived = 0; byte[] bufferTmp = new byte[tmpBufferSize]; try { while (conn.client.Connected && conn.isActive) { if (conn.readyToSend && conn.stream.CanWrite && conn.messageQueue.Count > 0) { byte[] sendBuffer; lock (conn.messageQueue) { sendBuffer = conn.messageQueue.Dequeue().WrapMessage(); conn.readyToSend = false; } conn.stream.BeginWrite(sendBuffer, 0, sendBuffer.Length, new System.AsyncCallback(MessageSentToServer), conn); } bytesReceived = 0; while (conn.stream.DataAvailable) { conn.messageReceiveTime = (ulong)System.DateTime.Now.Ticks; bytesReceived = conn.stream.Read(bufferTmp, 0, bufferTmp.Length); ProcessReceivedData(bufferTmp, bytesReceived, conn); } Thread.Sleep(KinectInterop.THREAD_SLEEP_TIME_MS); } } catch(ThreadAbortException) { // do nothing } catch (System.Exception ex) { Debug.LogException(ex); } conn.isActive = false; conn.messageQueue.Clear(); //Debug.Log("Connection " + conn.ID + " to " + conn.remoteEP + " closed."); LogToConsole("Connection to " + streamDesc + " server closed."); conn.stream.Close(); conn.client.Close(); if (connections.Contains(conn)) { connections.Remove(conn); } } private void MessageSentToServer(System.IAsyncResult ar) { var conn = (NetConnData)ar.AsyncState; try { conn.stream.EndWrite(ar); } catch (System.Exception ex) { if(conn.isActive) { //Debug.LogError("Exception occured while sending message to server."); LogErrorToConsole("Error sending to " + streamDesc + " server: " + ex.Message); //Debug.LogException(ex); conn.isActive = false; } } conn.readyToSend = true; } private void ProcessReceivedData(byte[] buffer, int bytesReceived, NetConnData conn) { if (conn.bytesReceived == 0 && bytesReceived > 3) //new message { int newMessageSize = System.BitConverter.ToInt32(buffer, 0) + sizeof(int); //prefix is one int if (newMessageSize != conn.messageSize) { conn.messageSize = newMessageSize; conn.messageBuffer = new byte[newMessageSize]; } } int availableLength = conn.messageSize - conn.bytesReceived; int copyLen = Mathf.Min(availableLength, bytesReceived); conn.packetCounter++; System.Array.Copy(buffer, 0, conn.messageBuffer, conn.bytesReceived, copyLen); conn.bytesReceived += copyLen; if (conn.bytesReceived == conn.messageSize) { conn.ResetCounters(); NetMessageData message = new NetMessageData(); message.SetDecompressor(decompressor); message.UnwrapMessage(conn.messageBuffer); //Debug.Log("Received message: " + message.timestamp + " - " + message.msgType + " @ " + System.DateTime.Now.ToString("HH:mm:ss.fff")); ReceivedMessage?.Invoke(conn, new ReceivedMessageEventArgs(message)); } if (copyLen != bytesReceived) // process the remainder of the message { byte[] newBuffer = new byte[bytesReceived - copyLen]; System.Array.Copy(buffer, copyLen, newBuffer, 0, bytesReceived - copyLen); ProcessReceivedData(newBuffer, bytesReceived - copyLen, conn); } } // logs message to the console private void LogToConsole(string sMessage) { Debug.Log(sMessage); lock (sbConsole) { sbConsole.Clear(); sbConsole.Append(sMessage); //.AppendLine(); } } // logs error message to the console private void LogErrorToConsole(string sMessage) { Debug.LogError(sMessage); lock (sbConsole) { sbConsole.Clear(); sbConsole.Append(sMessage); //.AppendLine(); } } // logs error message to the console private void LogErrorToConsole(System.Exception ex) { LogErrorToConsole(ex.Message + "\n" + ex.StackTrace); } } }