Add a basic PoseStreamer implementation for streaming mocap data

This commit is contained in:
ButterscotchVanilla
2021-09-22 00:49:46 -04:00
parent e7f81eb1aa
commit 472fcab821
3 changed files with 140 additions and 7 deletions

View File

@@ -0,0 +1,39 @@
package dev.slimevr.poserecorder;
import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import io.eiren.vr.processor.HumanSkeleton;
public abstract class PoseFileStream implements AutoCloseable {
protected DataOutputStream dataStream;
protected PoseFileStream(OutputStream outputStream) {
this.dataStream = new DataOutputStream(new BufferedOutputStream(outputStream));
}
protected PoseFileStream(File file) throws FileNotFoundException {
this(new FileOutputStream(file));
}
protected PoseFileStream(String file) throws FileNotFoundException {
this(new FileOutputStream(file));
}
abstract boolean writeHeader(HumanSkeleton skeleton) throws IOException;
abstract boolean writeFrame(HumanSkeleton skeleton) throws IOException;
abstract boolean writeFooter(HumanSkeleton skeleton) throws IOException;
@Override
public void close() throws IOException {
dataStream.close();
}
}

View File

@@ -67,16 +67,16 @@ public class PoseRecorder {
}
}
public synchronized Future<PoseFrames> startFrameRecording(int numFrames, long interval) {
return startFrameRecording(numFrames, interval, server.getAllTrackers());
public synchronized Future<PoseFrames> startFrameRecording(int numFrames, long intervalMs) {
return startFrameRecording(numFrames, intervalMs, server.getAllTrackers());
}
public synchronized Future<PoseFrames> startFrameRecording(int numFrames, long interval, List<Tracker> trackers) {
public synchronized Future<PoseFrames> startFrameRecording(int numFrames, long intervalMs, List<Tracker> trackers) {
if(numFrames < 1) {
throw new IllegalArgumentException("numFrames must at least have a value of 1");
}
if(interval < 1) {
throw new IllegalArgumentException("interval must at least have a value of 1");
if(intervalMs < 1) {
throw new IllegalArgumentException("intervalMs must at least have a value of 1");
}
if(trackers == null) {
throw new IllegalArgumentException("trackers must not be null");
@@ -107,10 +107,10 @@ public class PoseRecorder {
this.frameCursor = 0;
this.numFrames = numFrames;
frameRecordingInterval = interval;
frameRecordingInterval = intervalMs;
nextFrameTimeMs = -1L;
LogManager.log.info("[PoseRecorder] Recording " + numFrames + " samples at a " + interval + " ms frame interval");
LogManager.log.info("[PoseRecorder] Recording " + numFrames + " samples at a " + intervalMs + " ms frame interval");
currentRecording = new CompletableFuture<PoseFrames>();
return currentRecording;

View File

@@ -0,0 +1,94 @@
package dev.slimevr.poserecorder;
import java.io.IOException;
import io.eiren.util.ann.VRServerThread;
import io.eiren.util.logging.LogManager;
import io.eiren.vr.VRServer;
import io.eiren.vr.processor.HumanSkeleton;
public class PoseStreamer {
protected long frameRecordingInterval = 60L;
protected long nextFrameTimeMs = -1L;
private HumanSkeleton skeleton;
private PoseFileStream poseFileStream;
protected final VRServer server;
public PoseStreamer(VRServer server) {
this.server = server;
// Register callbacks/events
server.addSkeletonUpdatedCallback(this::onSkeletonUpdated);
server.addOnTick(this::onTick);
}
@VRServerThread
public void onSkeletonUpdated(HumanSkeleton skeleton) {
this.skeleton = skeleton;
}
@VRServerThread
public void onTick() {
PoseFileStream poseFileStream = this.poseFileStream;
if (poseFileStream != null) {
long curTime = System.currentTimeMillis();
if (curTime >= nextFrameTimeMs) {
nextFrameTimeMs += frameRecordingInterval;
// To prevent duplicate frames, make sure the frame time is always in the future
if (nextFrameTimeMs <= curTime) {
nextFrameTimeMs = curTime + frameRecordingInterval;
}
try {
poseFileStream.writeFrame(skeleton);
} catch (Exception e) {
// Handle any exceptions without crashing the program
LogManager.log.severe("[PoseStreamer] Exception while saving frame", e);
}
}
}
}
public void setFrameInterval(long intervalMs) {
if(intervalMs < 1) {
throw new IllegalArgumentException("intervalMs must at least have a value of 1");
}
this.frameRecordingInterval = intervalMs;
}
public void setOutput(PoseFileStream poseFileStream) throws IOException {
poseFileStream.writeHeader(skeleton);
this.poseFileStream = poseFileStream;
nextFrameTimeMs = -1L; // Reset the frame timing
}
public PoseFileStream getOutput() {
return poseFileStream;
}
public void closeOutput() {
PoseFileStream poseFileStream = this.poseFileStream;
if (poseFileStream != null) {
try {
poseFileStream.writeFooter(skeleton);
} catch (Exception e) {
// Ignore
LogManager.log.severe("[PoseStreamer] Exception while writing file footer", e);
}
try {
poseFileStream.close();
} catch (Exception e) {
// Ignore
LogManager.log.severe("[PoseStreamer] Exception while closing file stream", e);
}
}
}
}