Guarantee cameras always have the same ports (#123)

* Guarantee cameras always have the same ports

* clean up cscore stuff

* Fix stream api abuse
This commit is contained in:
Matt
2020-09-15 19:00:26 -07:00
committed by GitHub
parent 44bfc3ea6c
commit 24132555b8
4 changed files with 136 additions and 17 deletions

View File

@@ -52,6 +52,8 @@ public class CameraConfiguration {
public int currentPipelineIndex = 0;
public Rotation2d camPitch = new Rotation2d();
public int streamIndex = 0; // 0 index means ports [1181, 1182], 1 means [1183, 1184], etc...
@JsonIgnore // this ignores the pipes as we serialize them to their own subfolder
public List<CVPipelineSettings> pipelineSettings = new ArrayList<>();

View File

@@ -17,10 +17,15 @@
package org.photonvision.vision.frame.consumer;
import edu.wpi.cscore.CameraServerJNI;
import edu.wpi.cscore.CvSource;
import edu.wpi.cscore.MjpegServer;
import edu.wpi.cscore.VideoEvent;
import edu.wpi.cscore.VideoListener;
import edu.wpi.cscore.VideoMode;
import edu.wpi.first.cameraserver.CameraServer;
import edu.wpi.first.networktables.NetworkTable;
import edu.wpi.first.networktables.NetworkTableInstance;
import java.util.ArrayList;
import org.opencv.core.Mat;
import org.opencv.core.Size;
import org.opencv.imgproc.Imgproc;
@@ -33,14 +38,63 @@ public class MJPGFrameConsumer {
private final MjpegServer mjpegServer;
private FrameDivisor divisor = FrameDivisor.NONE;
public MJPGFrameConsumer(String sourceName, int width, int height) {
// TODO h264?
@SuppressWarnings("FieldCanBeLocal")
private final VideoListener listener;
private final NetworkTable table;
public MJPGFrameConsumer(String sourceName, int width, int height, int port) {
this.cvSource = new CvSource(sourceName, VideoMode.PixelFormat.kMJPEG, width, height, 30);
this.mjpegServer = CameraServer.getInstance().startAutomaticCapture(cvSource);
this.table =
NetworkTableInstance.getDefault().getTable("/CameraPublisher").getSubTable(sourceName);
this.mjpegServer = new MjpegServer("serve_" + cvSource.getName(), port);
mjpegServer.setSource(cvSource);
listener =
new VideoListener(
event -> {
if (event.kind == VideoEvent.Kind.kNetworkInterfacesChanged) {
table.getEntry("source").setString("cv:");
table.getEntry("streams");
table.getEntry("connected").setBoolean(true);
table.getEntry("mode").setString(videoModeToString(cvSource.getVideoMode()));
table.getEntry("modes").setStringArray(getSourceModeValues(cvSource.getHandle()));
updateStreamValues();
}
},
0x4fff,
true);
}
public MJPGFrameConsumer(String name) {
this(name, 320, 240);
private synchronized void updateStreamValues() {
// Get port
int port = mjpegServer.getPort();
// Generate values
var addresses = CameraServerJNI.getNetworkInterfaces();
ArrayList<String> values = new ArrayList<>(addresses.length + 1);
String listenAddress = CameraServerJNI.getMjpegServerListenAddress(mjpegServer.getHandle());
if (!listenAddress.isEmpty()) {
// If a listen address is specified, only use that
values.add(makeStreamValue(listenAddress, port));
} else {
// Otherwise generate for hostname and all interface addresses
values.add(makeStreamValue(CameraServerJNI.getHostname() + ".local", port));
for (String addr : addresses) {
if ("127.0.0.1".equals(addr)) {
continue; // ignore localhost
}
values.add(makeStreamValue(addr, port));
}
}
String[] streamAddresses = values.toArray(new String[0]);
table.getEntry("streams").setStringArray(streamAddresses);
}
public MJPGFrameConsumer(String name, int port) {
this(name, 320, 240, port);
}
public void setFrameDivisor(FrameDivisor divisor) {
@@ -68,4 +122,45 @@ public class MJPGFrameConsumer {
public int getCurrentStreamPort() {
return mjpegServer.getPort();
}
private static String makeStreamValue(String address, int port) {
return "mjpg:http://" + address + ":" + port + "/?action=stream";
}
private static String[] getSourceModeValues(int sourceHandle) {
VideoMode[] modes = CameraServerJNI.enumerateSourceVideoModes(sourceHandle);
String[] modeStrings = new String[modes.length];
for (int i = 0; i < modes.length; i++) {
modeStrings[i] = videoModeToString(modes[i]);
}
return modeStrings;
}
private static String videoModeToString(VideoMode mode) {
return mode.width
+ "x"
+ mode.height
+ " "
+ pixelFormatToString(mode.pixelFormat)
+ " "
+ mode.fps
+ " fps";
}
private static String pixelFormatToString(VideoMode.PixelFormat pixelFormat) {
switch (pixelFormat) {
case kMJPEG:
return "MJPEG";
case kYUYV:
return "YUYV";
case kRGB565:
return "RGB565";
case kBGR:
return "BGR";
case kGray:
return "Gray";
default:
return "Unknown";
}
}
}

View File

@@ -95,12 +95,7 @@ public class VisionModule {
DataChangeService.getInstance().addSubscriber(new VisionModuleChangeSubscriber(this));
dashboardOutputStreamer =
new MJPGFrameConsumer(
visionSource.getSettables().getConfiguration().uniqueName + "-output");
dashboardInputStreamer =
new MJPGFrameConsumer(visionSource.getSettables().getConfiguration().uniqueName + "-input");
createStreams();
fpsLimitedResultConsumers.add(result -> dashboardInputStreamer.accept(result.inputFrame));
fpsLimitedResultConsumers.add(result -> dashboardOutputStreamer.accept(result.outputFrame));
@@ -140,6 +135,21 @@ public class VisionModule {
saveAndBroadcastAll();
}
private void createStreams() {
var camStreamIdx = visionSource.getSettables().getConfiguration().streamIndex;
// If idx = 0, we want (1181, 1182)
var inputStreamPort = 1181 + (camStreamIdx * 2);
var outputStreamPort = 1181 + (camStreamIdx * 2) + 1;
dashboardOutputStreamer =
new MJPGFrameConsumer(
visionSource.getSettables().getConfiguration().uniqueName + "-output",
outputStreamPort);
dashboardInputStreamer =
new MJPGFrameConsumer(
visionSource.getSettables().getConfiguration().uniqueName + "-input", inputStreamPort);
}
void setDriverMode(boolean isDriverMode) {
pipelineManager.setDriverMode(isDriverMode);
setVisionLEDs(!isDriverMode);
@@ -271,11 +281,7 @@ public class VisionModule {
// rename streams
fpsLimitedResultConsumers.clear();
dashboardOutputStreamer =
new MJPGFrameConsumer(
visionSource.getSettables().getConfiguration().uniqueName + "-output");
dashboardInputStreamer =
new MJPGFrameConsumer(visionSource.getSettables().getConfiguration().uniqueName + "-input");
createStreams();
fpsLimitedResultConsumers.add(result -> dashboardInputStreamer.accept(result.inputFrame));
fpsLimitedResultConsumers.add(result -> dashboardOutputStreamer.accept(result.outputFrame));

View File

@@ -57,10 +57,26 @@ public class VisionModuleManager {
for (var entry : visionSources.entrySet()) {
var visionSource = entry.getKey();
var pipelineManager = new PipelineManager(entry.getValue());
assignCameraIndex(visionSource.getSettables().getConfiguration());
var module = new VisionModule(pipelineManager, visionSource, visionModules.size());
visionModules.add(module);
addedModules.add(module);
}
return addedModules;
}
private void assignCameraIndex(CameraConfiguration config) {
var max =
visionModules.stream()
.mapToInt(it -> it.visionSource.getSettables().getConfiguration().streamIndex)
.max()
.orElse(-1);
// If the current stream index is reserved, increase by 1
if (config.streamIndex <= max) {
config.streamIndex = max + 1;
}
}
}