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<float> LatencyValues = new LinkedList<float>();
|
|
|
|
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<PacketLatencyPair> packetQueue = new LinkedList<PacketLatencyPair>();
|
|
|
|
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<PacketLatencyPair> deadList = new List<PacketLatencyPair>();
|
|
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<OvrAvatarRemoteDriver>().QueuePacket(sequence, avatarPacket);
|
|
}
|
|
}
|
|
}
|