public IEnumerator Prepare(Material azureKinectScreenMaterial, VideoSenderMessage videoMessage)
    {
        if (State != PrepareState.Unprepared)
        {
            throw new Exception("State has to be Unprepared to prepare TextureGroupUpdater.");
        }

        State = PrepareState.Preparing;

        textureSet.SetWidth(videoMessage.width);
        textureSet.SetHeight(videoMessage.height);
        TelepresenceToolkitPlugin.InitTextureGroup(textureSet.GetId());

        colorDecoder = new Vp8Decoder();
        depthDecoder = new TrvlDecoder(videoMessage.width * videoMessage.height);

        State = PrepareState.Prepared;

        while (!textureSet.IsInitialized())
        {
            yield return(null);
        }

        // TextureGroup includes Y, U, V, and a depth texture.
        azureKinectScreenMaterial.SetTexture("_YTex", textureSet.GetYTexture());
        azureKinectScreenMaterial.SetTexture("_UvTex", textureSet.GetUvTexture());
        azureKinectScreenMaterial.SetTexture("_DepthTex", textureSet.GetDepthTexture());

        State = PrepareState.Prepared;
    }
Exemplo n.º 2
0
    // Since calculation including Unproject() takes too much time, this function is made to run as a coroutine.
    public IEnumerator Prepare(VideoSenderMessage videoMessage)
    {
        State    = PrepareState.Preparing;
        Progress = 0.0f;

        int width  = videoMessage.width;
        int height = videoMessage.height;

        var vertices = new Vector3[width * height];
        var uv       = new Vector2[width * height];

        for (int i = 0; i < width; ++i)
        {
            for (int j = 0; j < height; ++j)
            {
                float[] xy    = new float[2];
                int     valid = 0;
                // TODO: Check whether using videoMessageData.intrinsics.maxRadiusForProjection is correct.
                if (KinectSolver.Unproject(videoMessage.intrinsics,
                                           videoMessage.intrinsics.maxRadiusForProjection,
                                           new float[2] {
                    i, j
                }, ref xy, ref valid))
                {
                    // Flip y since Azure Kinect's y axis is downwards.
                    // https://docs.microsoft.com/en-us/azure/kinect-dk/coordinate-systems
                    vertices[i + j * width] = new Vector3(xy[0], -xy[1], 1.0f);
                }
                else
                {
                    vertices[i + j * width] = new Vector3(0.0f, 0.0f, 0.0f);
                }
                uv[i + j * width] = new Vector2(i / (float)(width - 1), j / (float)(height - 1));
            }

            Progress = i / (float)width * 0.99f;
            yield return(null);
        }

        Progress = 0.99f;

        //print($"vertices[0]: {vertices[0]}"); // (-1.0, 1.0, 1.0): left-top
        //print($"vertices[last]: {vertices[vertices.Length - 1]}"); // (0.8, -0.6, 1.0): right-bottom

        const float SIZE_AMPLIFIER = 1.2f;
        int         quadWidth      = width - 2;
        int         quadHeight     = height - 2;
        var         quadVertices   = new Vector3[quadWidth * quadHeight];
        var         quadUv         = new Vector2[quadWidth * quadHeight];
        var         quadHalfSizes  = new Vector2[quadWidth * quadHeight];

        for (int ii = 0; ii < quadWidth; ++ii)
        {
            for (int jj = 0; jj < quadHeight; ++jj)
            {
                int i = ii + 1;
                int j = jj + 1;
                quadVertices[ii + jj * quadWidth] = vertices[i + j * width];
                quadUv[ii + jj * quadWidth]       = uv[i + j * width];
                // Trying to make both x and y to have a positive number. The first 0.5f is to make the size relevant to
                // the vertex in (i, j). The second one is to get the half size of it.
                quadHalfSizes[ii + jj * quadWidth] = (vertices[(i + 1) + (j - 1) * width] - vertices[(i - 1) + (j + 1) * width]) * 0.5f * 0.5f * SIZE_AMPLIFIER;
            }
        }

        //print($"quadSizes[0]: {quadSizes[0].x}, {quadSizes[0].y}"); // 0.002900749, 0.003067017

        var triangles = new int[quadWidth * quadHeight];

        for (int i = 0; i < quadWidth * quadHeight; ++i)
        {
            triangles[i] = i;
        }

        // 65.535 is equivalent to (2^16 - 1) / 1000, where (2^16 - 1) is to complement
        // the conversion happened in the texture-level from 0 ~ (2^16 - 1) to 0 ~ 1.
        // 1000 is the conversion of mm (the unit of Azure Kinect) to m (the unit of Unity3D).
        for (int i = 0; i < quadVertices.Length; ++i)
        {
            quadVertices[i] *= 65.535f;
        }

        for (int i = 0; i < quadHalfSizes.Length; ++i)
        {
            quadHalfSizes[i] *= 65.535f;
        }

        // Without the bounds, Unity decides whether to render this mesh or not based on the vertices calculated here.
        // This causes Unity not rendering the mesh transformed by the depth texture even when the transformed one
        // belongs to the viewport of the camera.
        var bounds = new Bounds(Vector3.zero, Vector3.one * 1000.0f);

        var mesh = new Mesh()
        {
            indexFormat = IndexFormat.UInt32,
            vertices    = quadVertices,
            uv          = quadUv,
            uv2         = quadHalfSizes,
            bounds      = bounds,
        };

        mesh.SetIndices(triangles, MeshTopology.Points, 0);

        meshFilter.mesh = mesh;

        State = PrepareState.Prepared;
    }
Exemplo n.º 3
0
    public void Assemble(UdpSocket udpSocket,
                         List <VideoSenderPacket> videoPacketDataList,
                         List <ParitySenderPacket> parityPacketDataList,
                         int lastVideoFrameId,
                         IDictionary <int, VideoSenderMessage> videoMessages)
    {
        int?addedFrameId = null;

        // Collect the received video packets.
        foreach (var videoSenderPacketData in videoPacketDataList)
        {
            if (videoSenderPacketData.frameId <= lastVideoFrameId)
            {
                continue;
            }

            if (!videoPacketCollections.ContainsKey(videoSenderPacketData.frameId))
            {
                videoPacketCollections[videoSenderPacketData.frameId] = new VideoSenderPacket[videoSenderPacketData.packetCount];
                // Assign the largest new frame_id.
                if (!addedFrameId.HasValue || addedFrameId < videoSenderPacketData.frameId)
                {
                    addedFrameId = videoSenderPacketData.frameId;
                }
            }

            videoPacketCollections[videoSenderPacketData.frameId][videoSenderPacketData.packetIndex] = videoSenderPacketData;
        }

        // Collect the received parity packets.
        foreach (var paritySenderPacketData in parityPacketDataList)
        {
            if (paritySenderPacketData.frameId <= lastVideoFrameId)
            {
                continue;
            }

            if (!parityPacketCollections.ContainsKey(paritySenderPacketData.frameId))
            {
                int parityPacketCount = (paritySenderPacketData.videoPacketCount - 1) / FEC_GROUP_SIZE + 1;
                parityPacketCollections[paritySenderPacketData.frameId] = new ParitySenderPacket[parityPacketCount];
            }

            parityPacketCollections[paritySenderPacketData.frameId][paritySenderPacketData.packetIndex] = paritySenderPacketData;
        }

        if (addedFrameId.HasValue)
        {
            foreach (var videoPacketCollection in videoPacketCollections)
            {
                int frameId = videoPacketCollection.Key;
                VideoSenderPacket[] videoPackets = videoPacketCollection.Value;

                // Skip the frame that just got added or even newer.
                if (frameId >= addedFrameId)
                {
                    continue;
                }

                // Find the parity packet collection corresponding to the video packet collection.
                // Skip if there is no parity packet collection for the video frame.
                if (!parityPacketCollections.ContainsKey(frameId))
                {
                    continue;
                }

                ParitySenderPacket[] parityPackets = parityPacketCollections[frameId];

                // Loop per each parity packet.
                // Collect video packet indices to request.
                List <int> videoPacketIndiecsToRequest  = new List <int>();
                List <int> parityPacketIndiecsToRequest = new List <int>();

                for (int parityPacketIndex = 0; parityPacketIndex < parityPackets.Length; ++parityPacketIndex)
                {
                    // Range of the video packets that correspond to the parity packet.
                    int videoPacketStartIndex = parityPacketIndex * FEC_GROUP_SIZE;
                    // Pick the end index with the end of video packet indices in mind (i.e., prevent overflow).
                    int videoPacketEndIndex = Math.Min(videoPacketStartIndex + FEC_GROUP_SIZE, videoPackets.Length);

                    // If the parity packet is missing, request all missing video packets and skip the FEC process.
                    // Also request the parity packet if there is a relevant missing video packet.
                    if (parityPackets[parityPacketIndex] == null)
                    {
                        bool parityPacketNeeded = false;
                        for (int videoPacketIndex = videoPacketStartIndex; videoPacketIndex < videoPacketEndIndex; ++videoPacketIndex)
                        {
                            if (videoPackets[videoPacketIndex] == null)
                            {
                                videoPacketIndiecsToRequest.Add(videoPacketIndex);
                                parityPacketNeeded = true;
                            }
                        }
                        if (parityPacketNeeded)
                        {
                            parityPacketIndiecsToRequest.Add(parityPacketIndex);
                        }
                        continue;
                    }

                    // Find if there is existing video packets and missing video packet indices.
                    // Check all video packets that relates to this parity packet.
                    var existingVideoPackets      = new List <VideoSenderPacket>();
                    var missingVideoPacketIndices = new List <int>();
                    for (int videoPacketIndex = videoPacketStartIndex; videoPacketIndex < videoPacketEndIndex; ++videoPacketIndex)
                    {
                        if (videoPackets[videoPacketIndex] != null)
                        {
                            existingVideoPackets.Add(videoPackets[videoPacketIndex]);
                        }
                        else
                        {
                            missingVideoPacketIndices.Add(videoPacketIndex);
                        }
                    }

                    // Skip if there all video packets already exist.
                    if (missingVideoPacketIndices.Count == 0)
                    {
                        continue;
                    }

                    // XOR based FEC only works for a single missing packet.
                    if (missingVideoPacketIndices.Count > 1)
                    {
                        foreach (int missingIndex in missingVideoPacketIndices)
                        {
                            // Add the missing video packet indices for the vector to request them.
                            videoPacketIndiecsToRequest.Add(missingIndex);
                        }
                        continue;
                    }

                    // The missing video packet index.
                    int missingVideoPacketIndex = missingVideoPacketIndices[0];

                    // Reconstruct the missing video packet.
                    VideoSenderPacket fecVideoPacketData = new VideoSenderPacket();
                    fecVideoPacketData.frameId     = frameId;
                    fecVideoPacketData.packetIndex = missingVideoPacketIndex;
                    fecVideoPacketData.packetCount = videoPackets.Length;
                    // Assign from the parity packet here since other video packets will be XOR'ed in the below loop.
                    fecVideoPacketData.messageData = parityPackets[parityPacketIndex].bytes;

                    foreach (var existingVideoPacket in existingVideoPackets)
                    {
                        for (int i = 0; i < fecVideoPacketData.messageData.Length; ++i)
                        {
                            fecVideoPacketData.messageData[i] ^= existingVideoPacket.messageData[i];
                        }
                    }

                    // Insert the reconstructed packet.
                    videoPacketCollections[frameId][missingVideoPacketIndex] = fecVideoPacketData;
                }
                // Request the video packets that FEC was not enough to fix.
                udpSocket.Send(PacketUtils.createRequestReceiverPacketBytes(sessionId, frameId, false, videoPacketIndiecsToRequest, parityPacketIndiecsToRequest).bytes, remoteEndPoint);
            }
        }

        // Find all full collections and their frame_ids.
        var fullFrameIds = new List <int>();

        foreach (var collectionPair in videoPacketCollections)
        {
            bool full = true;
            foreach (var packetData in collectionPair.Value)
            {
                if (packetData == null)
                {
                    full = false;
                    break;
                }
            }

            if (full)
            {
                int frameId = collectionPair.Key;
                fullFrameIds.Add(frameId);
            }
        }

        // Extract messages from the full collections.
        foreach (int fullFrameId in fullFrameIds)
        {
            var ms = new MemoryStream();
            foreach (var packetData in videoPacketCollections[fullFrameId])
            {
                ms.Write(packetData.messageData, 0, packetData.messageData.Length);
            }

            var videoMessageData = VideoSenderMessage.Create(ms.ToArray());
            videoMessages.Add(fullFrameId, videoMessageData);

            videoPacketCollections.Remove(fullFrameId);
        }

        // Clean up frame_packet_collections.
        var obsoleteFrameIds = new List <int>();

        foreach (var collectionPair in videoPacketCollections)
        {
            if (collectionPair.Key <= lastVideoFrameId)
            {
                obsoleteFrameIds.Add(collectionPair.Key);
            }
        }

        foreach (int obsoleteFrameId in obsoleteFrameIds)
        {
            videoPacketCollections.Remove(obsoleteFrameId);
        }
    }