diff --git a/wpilibc/athena/include/CameraServer.h b/wpilibc/athena/include/CameraServer.h
index d5dcb2f88c..1d60db2a8c 100644
--- a/wpilibc/athena/include/CameraServer.h
+++ b/wpilibc/athena/include/CameraServer.h
@@ -84,6 +84,100 @@ class CameraServer : public ErrorBase {
*/
void StartAutomaticCapture(const cs::VideoSource& camera);
+ /**
+ * Adds an Axis IP camera.
+ *
+ *
This overload calls {@link #AddAxisCamera(String, String)} with
+ * name "Axis Camera".
+ *
+ * @param host Camera host IP or DNS name (e.g. "10.x.y.11")
+ */
+ cs::AxisCamera AddAxisCamera(llvm::StringRef host);
+
+ /**
+ * Adds an Axis IP camera.
+ *
+ *
This overload calls {@link #AddAxisCamera(String, String)} with
+ * name "Axis Camera".
+ *
+ * @param host Camera host IP or DNS name (e.g. "10.x.y.11")
+ */
+ cs::AxisCamera AddAxisCamera(const char* host);
+
+ /**
+ * Adds an Axis IP camera.
+ *
+ *
This overload calls {@link #AddAxisCamera(String, String)} with
+ * name "Axis Camera".
+ *
+ * @param host Camera host IP or DNS name (e.g. "10.x.y.11")
+ */
+ cs::AxisCamera AddAxisCamera(const std::string& host);
+
+ /**
+ * Adds an Axis IP camera.
+ *
+ *
This overload calls {@link #AddAxisCamera(String, String[])} with
+ * name "Axis Camera".
+ *
+ * @param hosts Array of Camera host IPs/DNS names
+ */
+ cs::AxisCamera AddAxisCamera(llvm::ArrayRef hosts);
+
+ /**
+ * Adds an Axis IP camera.
+ *
+ * This overload calls {@link #AddAxisCamera(String, String[])} with
+ * name "Axis Camera".
+ *
+ * @param hosts Array of Camera host IPs/DNS names
+ */
+ template
+ cs::AxisCamera AddAxisCamera(std::initializer_list hosts);
+
+ /**
+ * Adds an Axis IP camera.
+ *
+ * @param name The name to give the camera
+ * @param host Camera host IP or DNS name (e.g. "10.x.y.11")
+ */
+ cs::AxisCamera AddAxisCamera(llvm::StringRef name, llvm::StringRef host);
+
+ /**
+ * Adds an Axis IP camera.
+ *
+ * @param name The name to give the camera
+ * @param host Camera host IP or DNS name (e.g. "10.x.y.11")
+ */
+ cs::AxisCamera AddAxisCamera(llvm::StringRef name, const char* host);
+
+ /**
+ * Adds an Axis IP camera.
+ *
+ * @param name The name to give the camera
+ * @param host Camera host IP or DNS name (e.g. "10.x.y.11")
+ */
+ cs::AxisCamera AddAxisCamera(llvm::StringRef name, const std::string& host);
+
+ /**
+ * Adds an Axis IP camera.
+ *
+ * @param name The name to give the camera
+ * @param hosts Array of Camera host IPs/DNS names
+ */
+ cs::AxisCamera AddAxisCamera(llvm::StringRef name,
+ llvm::ArrayRef hosts);
+
+ /**
+ * Adds an Axis IP camera.
+ *
+ * @param name The name to give the camera
+ * @param hosts Array of Camera host IPs/DNS names
+ */
+ template
+ cs::AxisCamera AddAxisCamera(llvm::StringRef name,
+ std::initializer_list hosts);
+
/**
* Get OpenCV access to the primary camera feed. This allows you to
* get images from the camera for image processing on the roboRIO.
@@ -176,6 +270,7 @@ class CameraServer : public ErrorBase {
CameraServer();
std::shared_ptr GetSourceTable(CS_Source source);
+ std::vector GetSinkStreamValues(CS_Sink sink);
void UpdateStreamValues();
static constexpr char const* kPublishName = "/CameraPublisher";
@@ -193,3 +288,5 @@ class CameraServer : public ErrorBase {
};
} // namespace frc
+
+#include "CameraServer.inc"
diff --git a/wpilibc/athena/include/CameraServer.inc b/wpilibc/athena/include/CameraServer.inc
new file mode 100644
index 0000000000..54e3639194
--- /dev/null
+++ b/wpilibc/athena/include/CameraServer.inc
@@ -0,0 +1,30 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2016. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#pragma once
+
+#include
+#include
+
+namespace frc {
+
+template
+inline cs::AxisCamera CameraServer::AddAxisCamera(
+ std::initializer_list hosts) {
+ return AddAxisCamera("Axis Camera", hosts);
+}
+
+template
+inline cs::AxisCamera CameraServer::AddAxisCamera(
+ llvm::StringRef name, std::initializer_list hosts) {
+ std::vector vec;
+ vec.reserve(hosts.size());
+ for (const auto& host : hosts) vec.emplace_back(host);
+ return AddAxisCamera(name, vec);
+}
+
+} // namespace frc
diff --git a/wpilibc/athena/src/CameraServer.cpp b/wpilibc/athena/src/CameraServer.cpp
index 02539781bf..f82ded3db6 100644
--- a/wpilibc/athena/src/CameraServer.cpp
+++ b/wpilibc/athena/src/CameraServer.cpp
@@ -34,7 +34,8 @@ static llvm::StringRef MakeSourceValue(CS_Source source,
case cs::VideoSource::kHttp: {
llvm::StringRef prefix{"ip:"};
buf.append(prefix.begin(), prefix.end());
- // TODO
+ auto urls = cs::GetHttpCameraUrls(source, &status);
+ if (!urls.empty()) buf.append(urls[0].begin(), urls[0].end());
break;
}
case cs::VideoSource::kCv:
@@ -61,41 +62,78 @@ std::shared_ptr CameraServer::GetSourceTable(CS_Source source) {
return m_tables.lookup(source);
}
+std::vector CameraServer::GetSinkStreamValues(CS_Sink sink) {
+ CS_Status status = 0;
+
+ // Ignore all but MjpegServer
+ if (cs::GetSinkKind(sink, &status) != CS_SINK_MJPEG)
+ return std::vector{};
+
+ // Get port
+ int port = cs::GetMjpegServerPort(sink, &status);
+
+ // Generate values
+ std::vector values;
+ auto listenAddress = cs::GetMjpegServerListenAddress(sink, &status);
+ if (!listenAddress.empty()) {
+ // If a listen address is specified, only use that
+ values.emplace_back(MakeStreamValue(listenAddress, port));
+ } else {
+ // Otherwise generate for hostname and all interface addresses
+ values.emplace_back(MakeStreamValue(cs::GetHostname() + ".local", port));
+
+ for (const auto& addr : m_addresses) {
+ if (addr == "127.0.0.1") continue; // ignore localhost
+ values.emplace_back(MakeStreamValue(addr, port));
+ }
+ }
+
+ return values;
+}
+
+static std::vector GetSourceStreamValues(CS_Source source) {
+ CS_Status status = 0;
+
+ // Ignore all but HttpCamera
+ if (cs::GetSourceKind(source, &status) != CS_SOURCE_HTTP)
+ return std::vector{};
+
+ // Generate values
+ auto values = cs::GetHttpCameraUrls(source, &status);
+ for (auto& value : values) value = "mjpg:" + value;
+
+ // Set table value
+ return values;
+}
+
void CameraServer::UpdateStreamValues() {
std::lock_guard lock(m_mutex);
// Over all the sinks...
for (const auto& i : m_sinks) {
CS_Status status = 0;
- // Ignore all but MjpegServer
- if (i.second.GetKind() != cs::VideoSink::kMjpeg) continue;
CS_Sink sink = i.second.GetHandle();
// Get the source's subtable (if none exists, we're done)
CS_Source source = cs::GetSinkSource(sink, &status);
auto table = m_tables.lookup(source);
- if (!table) continue;
-
- // Get port
- int port = cs::GetMjpegServerPort(sink, &status);
-
- // Generate values
- std::vector values;
- auto listenAddress = cs::GetMjpegServerListenAddress(sink, &status);
- if (!listenAddress.empty()) {
- // If a listen address is specified, only use that
- values.emplace_back(MakeStreamValue(listenAddress, port));
- } else {
- // Otherwise generate for hostname and all interface addresses
- values.emplace_back(MakeStreamValue(cs::GetHostname() + ".local", port));
-
- for (const auto& addr : m_addresses) {
- if (addr == "127.0.0.1") continue; // ignore localhost
- values.emplace_back(MakeStreamValue(addr, port));
- }
+ if (table) {
+ // Set table value
+ auto values = GetSinkStreamValues(sink);
+ if (!values.empty()) table->PutStringArray("streams", values);
}
+ }
- // Set table value
- table->PutStringArray("streams", values);
+ // Over all the sources...
+ for (const auto& i : m_sources) {
+ CS_Source source = i.second.GetHandle();
+
+ // Get the source's subtable (if none exists, we're done)
+ auto table = m_tables.lookup(source);
+ if (table) {
+ // Set table value
+ auto values = GetSourceStreamValues(source);
+ if (!values.empty()) table->PutStringArray("streams", values);
+ }
}
}
@@ -129,7 +167,8 @@ CameraServer::CameraServer()
cs::GetSourceDescription(event.sourceHandle, descBuf, &status));
table->PutBoolean("connected", cs::IsSourceConnected(
event.sourceHandle, &status));
- table->PutStringArray("streams", std::vector{});
+ table->PutStringArray("streams",
+ GetSourceStreamValues(event.sourceHandle));
break;
}
case cs::VideoEvent::kSourceDestroyed: {
@@ -227,6 +266,50 @@ cs::UsbCamera CameraServer::StartAutomaticCapture(llvm::StringRef name,
return camera;
}
+cs::AxisCamera CameraServer::AddAxisCamera(llvm::StringRef host) {
+ return AddAxisCamera("Axis Camera", host);
+}
+
+cs::AxisCamera CameraServer::AddAxisCamera(const char* host) {
+ return AddAxisCamera("Axis Camera", host);
+}
+
+cs::AxisCamera CameraServer::AddAxisCamera(const std::string& host) {
+ return AddAxisCamera("Axis Camera", host);
+}
+
+cs::AxisCamera CameraServer::AddAxisCamera(llvm::ArrayRef hosts) {
+ return AddAxisCamera("Axis Camera", hosts);
+}
+
+cs::AxisCamera CameraServer::AddAxisCamera(llvm::StringRef name,
+ llvm::StringRef host) {
+ cs::AxisCamera camera{name, host};
+ AddCamera(camera);
+ return camera;
+}
+
+cs::AxisCamera CameraServer::AddAxisCamera(llvm::StringRef name,
+ const char* host) {
+ cs::AxisCamera camera{name, host};
+ AddCamera(camera);
+ return camera;
+}
+
+cs::AxisCamera CameraServer::AddAxisCamera(llvm::StringRef name,
+ const std::string& host) {
+ cs::AxisCamera camera{name, host};
+ AddCamera(camera);
+ return camera;
+}
+
+cs::AxisCamera CameraServer::AddAxisCamera(llvm::StringRef name,
+ llvm::ArrayRef hosts) {
+ cs::AxisCamera camera{name, hosts};
+ AddCamera(camera);
+ return camera;
+}
+
void CameraServer::StartAutomaticCapture(const cs::VideoSource& camera) {
llvm::SmallString<64> name{"serve_"};
name += camera.GetName();
diff --git a/wpilibj/src/athena/java/edu/wpi/first/wpilibj/CameraServer.java b/wpilibj/src/athena/java/edu/wpi/first/wpilibj/CameraServer.java
index 8f60d3c300..bbf5a9fa93 100644
--- a/wpilibj/src/athena/java/edu/wpi/first/wpilibj/CameraServer.java
+++ b/wpilibj/src/athena/java/edu/wpi/first/wpilibj/CameraServer.java
@@ -7,6 +7,7 @@
package edu.wpi.first.wpilibj;
+import edu.wpi.cscore.AxisCamera;
import edu.wpi.cscore.CameraServerJNI;
import edu.wpi.cscore.CvSink;
import edu.wpi.cscore.CvSource;
@@ -65,9 +66,14 @@ public class CameraServer {
switch (VideoSource.getKindFromInt(CameraServerJNI.getSourceKind(source))) {
case kUsb:
return "usb:" + CameraServerJNI.getUsbCameraPath(source);
- case kHttp:
- // TODO
- return "ip:";
+ case kHttp: {
+ String[] urls = CameraServerJNI.getHttpCameraUrls(source);
+ if (urls.length > 0) {
+ return "ip:" + urls[0];
+ } else {
+ return "ip:";
+ }
+ }
case kCv:
// FIXME: Should be "cv:", but LabVIEW dashboard requires "usb:".
// https://github.com/wpilibsuite/allwpilib/issues/407
@@ -87,45 +93,84 @@ public class CameraServer {
return m_tables.get(source);
}
+ @SuppressWarnings({"JavadocMethod", "PMD.AvoidUsingHardCodedIP"})
+ private synchronized String[] getSinkStreamValues(int sink) {
+ // Ignore all but MjpegServer
+ if (VideoSink.getKindFromInt(CameraServerJNI.getSinkKind(sink)) != VideoSink.Kind.kMjpeg) {
+ return new String[0];
+ }
+
+ // Get port
+ int port = CameraServerJNI.getMjpegServerPort(sink);
+
+ // Generate values
+ ArrayList values = new ArrayList(m_addresses.length + 1);
+ String listenAddress = CameraServerJNI.getMjpegServerListenAddress(sink);
+ 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 : m_addresses) {
+ if (addr.equals("127.0.0.1")) {
+ continue; // ignore localhost
+ }
+ values.add(makeStreamValue(addr, port));
+ }
+ }
+
+ return values.toArray(new String[0]);
+ }
+
+ @SuppressWarnings("JavadocMethod")
+ private static String[] getSourceStreamValues(int source) {
+ // Ignore all but HttpCamera
+ if (VideoSource.getKindFromInt(CameraServerJNI.getSourceKind(source))
+ != VideoSource.Kind.kHttp) {
+ return new String[0];
+ }
+
+ // Generate values
+ String[] values = CameraServerJNI.getHttpCameraUrls(source);
+ for (int j = 0; j < values.length; j++) {
+ values[j] = "mjpg:" + values[j];
+ }
+
+ return values;
+ }
+
@SuppressWarnings({"JavadocMethod", "PMD.AvoidUsingHardCodedIP"})
private synchronized void updateStreamValues() {
// Over all the sinks...
for (VideoSink i : m_sinks.values()) {
- // Ignore all but MjpegServer
- if (i.getKind() != VideoSink.Kind.kMjpeg) {
- continue;
- }
int sink = i.getHandle();
// Get the source's subtable (if none exists, we're done)
int source = CameraServerJNI.getSinkSource(sink);
ITable table = m_tables.get(source);
- if (table == null) {
- continue;
- }
-
- // Get port
- int port = CameraServerJNI.getMjpegServerPort(sink);
-
- // Generate values
- ArrayList values = new ArrayList(m_addresses.length + 1);
- String listenAddress = CameraServerJNI.getMjpegServerListenAddress(sink);
- 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 : m_addresses) {
- if (addr.equals("127.0.0.1")) {
- continue; // ignore localhost
- }
- values.add(makeStreamValue(addr, port));
+ if (table != null) {
+ // Set table value
+ String[] values = getSinkStreamValues(sink);
+ if (values.length > 0) {
+ table.putStringArray("streams", values);
}
}
+ }
- // Set table value
- table.putStringArray("streams", values.toArray(new String[0]));
+ // Over all the sources...
+ for (VideoSource i : m_sources.values()) {
+ int source = i.getHandle();
+
+ // Get the source's subtable (if none exists, we're done)
+ ITable table = m_tables.get(source);
+ if (table != null) {
+ // Set table value
+ String[] values = getSourceStreamValues(source);
+ if (values.length > 0) {
+ table.putStringArray("streams", values);
+ }
+ }
}
}
@@ -157,7 +202,7 @@ public class CameraServer {
table.putString("description",
CameraServerJNI.getSourceDescription(event.sourceHandle));
table.putBoolean("connected", CameraServerJNI.isSourceConnected(event.sourceHandle));
- table.putStringArray("streams", new String[0]);
+ table.putStringArray("streams", getSourceStreamValues(event.sourceHandle));
break;
}
case kSourceDestroyed: {
@@ -300,6 +345,54 @@ public class CameraServer {
server.setSource(camera);
}
+ /**
+ * Adds an Axis IP camera.
+ *
+ * This overload calls {@link #addAxisCamera(String, String)} with
+ * name "Axis Camera".
+ *
+ * @param host Camera host IP or DNS name (e.g. "10.x.y.11")
+ */
+ public AxisCamera addAxisCamera(String host) {
+ return addAxisCamera("Axis Camera", host);
+ }
+
+ /**
+ * Adds an Axis IP camera.
+ *
+ *
This overload calls {@link #addAxisCamera(String, String[])} with
+ * name "Axis Camera".
+ *
+ * @param hosts Array of Camera host IPs/DNS names
+ */
+ public AxisCamera addAxisCamera(String[] hosts) {
+ return addAxisCamera("Axis Camera", hosts);
+ }
+
+ /**
+ * Adds an Axis IP camera.
+ *
+ * @param name The name to give the camera
+ * @param host Camera host IP or DNS name (e.g. "10.x.y.11")
+ */
+ public AxisCamera addAxisCamera(String name, String host) {
+ AxisCamera camera = new AxisCamera(name, host);
+ addCamera(camera);
+ return camera;
+ }
+
+ /**
+ * Adds an Axis IP camera.
+ *
+ * @param name The name to give the camera
+ * @param hosts Array of Camera host IPs/DNS names
+ */
+ public AxisCamera addAxisCamera(String name, String[] hosts) {
+ AxisCamera camera = new AxisCamera(name, hosts);
+ addCamera(camera);
+ return camera;
+ }
+
/**
* Get OpenCV access to the primary camera feed. This allows you to
* get images from the camera for image processing on the roboRIO.