using UnityEngine; using System.Collections; using System; using System.IO; using Oculus.Avatar; using System.Runtime.InteropServices; using System.Collections.Generic; public class RemoteLoopbackManager : MonoBehaviour { class PacketLatencyPair { public byte[] PacketData; public float FakeLatency; }; public OvrAvatar LocalAvatar; public OvrAvatar LoopbackAvatar; [System.Serializable] public class SimulatedLatencySettings { [Range(0.0f, 0.5f)] public float FakeLatencyMax = 0.25f; //250 ms max latency [Range(0.0f, 0.5f)] public float FakeLatencyMin = 0.002f; //2ms min latency [Range(0.0f, 1.0f)] public float LatencyWeight = 0.25f; // How much the latest sample impacts the current latency [Range(0,10)] public int MaxSamples = 4; //How many samples in our window internal float AverageWindow = 0f; internal float LatencySum = 0f; internal LinkedList LatencyValues = new LinkedList(); public float NextValue() { AverageWindow = LatencySum / (float)LatencyValues.Count; float RandomLatency = UnityEngine.Random.Range(FakeLatencyMin, FakeLatencyMax); float FakeLatency = AverageWindow * (1f - LatencyWeight) + LatencyWeight * RandomLatency; if (LatencyValues.Count >= MaxSamples) { LatencySum -= LatencyValues.First.Value; LatencyValues.RemoveFirst(); } LatencySum += FakeLatency; LatencyValues.AddLast(FakeLatency); return FakeLatency; } }; public SimulatedLatencySettings LatencySettings = new SimulatedLatencySettings(); private int PacketSequence = 0; LinkedList packetQueue = new LinkedList(); void Start() { LocalAvatar.RecordPackets = true; LocalAvatar.PacketRecorded += OnLocalAvatarPacketRecorded; float FirstValue = UnityEngine.Random.Range(LatencySettings.FakeLatencyMin, LatencySettings.FakeLatencyMax); LatencySettings.LatencyValues.AddFirst(FirstValue); LatencySettings.LatencySum += FirstValue; } void OnLocalAvatarPacketRecorded(object sender, OvrAvatar.PacketEventArgs args) { using (MemoryStream outputStream = new MemoryStream()) { BinaryWriter writer = new BinaryWriter(outputStream); if (LocalAvatar.UseSDKPackets) { var size = CAPI.ovrAvatarPacket_GetSize(args.Packet.ovrNativePacket); byte[] data = new byte[size]; CAPI.ovrAvatarPacket_Write(args.Packet.ovrNativePacket, size, data); writer.Write(PacketSequence++); writer.Write(size); writer.Write(data); } else { writer.Write(PacketSequence++); args.Packet.Write(outputStream); } SendPacketData(outputStream.ToArray()); } } void Update() { if (packetQueue.Count > 0) { List deadList = new List(); foreach (var packet in packetQueue) { packet.FakeLatency -= Time.deltaTime; if (packet.FakeLatency < 0f) { ReceivePacketData(packet.PacketData); deadList.Add(packet); } } foreach (var packet in deadList) { packetQueue.Remove(packet); } } } void SendPacketData(byte[] data) { PacketLatencyPair PacketPair = new PacketLatencyPair(); PacketPair.PacketData = data; PacketPair.FakeLatency = LatencySettings.NextValue(); packetQueue.AddLast(PacketPair); } void ReceivePacketData(byte[] data) { using (MemoryStream inputStream = new MemoryStream(data)) { BinaryReader reader = new BinaryReader(inputStream); int sequence = reader.ReadInt32(); OvrAvatarPacket avatarPacket; if (LoopbackAvatar.UseSDKPackets) { int size = reader.ReadInt32(); byte[] sdkData = reader.ReadBytes(size); IntPtr packet = CAPI.ovrAvatarPacket_Read((UInt32)data.Length, sdkData); avatarPacket = new OvrAvatarPacket { ovrNativePacket = packet }; } else { avatarPacket = OvrAvatarPacket.Read(inputStream); } LoopbackAvatar.GetComponent().QueuePacket(sequence, avatarPacket); } } }