mirror of
https://github.com/PhotonVision/photonvision
synced 2026-06-20 00:51:41 +00:00
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:
@@ -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<>();
|
||||
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user