diff --git a/cscore/src/main/java/edu/wpi/cscore/CameraServerJNI.java b/cscore/src/main/java/edu/wpi/cscore/CameraServerJNI.java
index 70814f4f04..2d67f3dee4 100644
--- a/cscore/src/main/java/edu/wpi/cscore/CameraServerJNI.java
+++ b/cscore/src/main/java/edu/wpi/cscore/CameraServerJNI.java
@@ -79,7 +79,9 @@ public class CameraServerJNI {
public static native String getSourceName(int source);
public static native String getSourceDescription(int source);
public static native long getSourceLastFrameTime(int source);
+ public static native void setSourceConnectionStrategy(int source, int strategy);
public static native boolean isSourceConnected(int source);
+ public static native boolean isSourceEnabled(int source);
public static native int getSourceProperty(int source, String name);
public static native int[] enumerateSourceProperties(int source);
public static native VideoMode getSourceVideoMode(int source);
diff --git a/cscore/src/main/java/edu/wpi/cscore/VideoSource.java b/cscore/src/main/java/edu/wpi/cscore/VideoSource.java
index dde32fbc02..b7d6bd522c 100644
--- a/cscore/src/main/java/edu/wpi/cscore/VideoSource.java
+++ b/cscore/src/main/java/edu/wpi/cscore/VideoSource.java
@@ -28,6 +28,40 @@ public class VideoSource implements AutoCloseable {
}
}
+ /**
+ * Connection strategy.
+ */
+ public enum ConnectionStrategy {
+ /**
+ * Automatically connect or disconnect based on whether any sinks are
+ * connected to this source. This is the default behavior.
+ */
+ kAutoManage(0),
+
+ /**
+ * Try to keep the connection open regardless of whether any sinks are
+ * connected.
+ */
+ kKeepOpen(1),
+
+ /**
+ * Never open the connection. If this is set when the connection is open,
+ * close the connection.
+ */
+ kForceClose(2);
+
+ @SuppressWarnings("MemberName")
+ private final int value;
+
+ ConnectionStrategy(int value) {
+ this.value = value;
+ }
+
+ public int getValue() {
+ return value;
+ }
+ }
+
/**
* Convert from the numerical representation of kind to an enum type.
*
@@ -118,6 +152,20 @@ public class VideoSource implements AutoCloseable {
return CameraServerJNI.getSourceLastFrameTime(m_handle);
}
+ /**
+ * Sets the connection strategy. By default, the source will automatically
+ * connect or disconnect based on whether any sinks are connected.
+ *
+ *
This function is non-blocking; look for either a connection open or
+ * close event or call {@link #isConnected()} to determine the connection
+ * state.
+ *
+ * @param strategy connection strategy (auto, keep open, or force close)
+ */
+ public void setConnectionStrategy(ConnectionStrategy strategy) {
+ CameraServerJNI.setSourceConnectionStrategy(m_handle, strategy.getValue());
+ }
+
/**
* Returns if the source currently connected to whatever is providing the images.
*/
@@ -125,6 +173,16 @@ public class VideoSource implements AutoCloseable {
return CameraServerJNI.isSourceConnected(m_handle);
}
+ /**
+ * Gets source enable status. This is determined with a combination of
+ * connection strategy and the number of sinks connected.
+ *
+ * @return True if enabled, false otherwise.
+ */
+ public boolean isEnabled() {
+ return CameraServerJNI.isSourceEnabled(m_handle);
+ }
+
/**
* Get a property.
*
diff --git a/cscore/src/main/native/cpp/HttpCameraImpl.cpp b/cscore/src/main/native/cpp/HttpCameraImpl.cpp
index 17456ad2c3..6052cd47b7 100644
--- a/cscore/src/main/native/cpp/HttpCameraImpl.cpp
+++ b/cscore/src/main/native/cpp/HttpCameraImpl.cpp
@@ -69,13 +69,12 @@ void HttpCameraImpl::StreamThreadMain() {
// sleep between retries
std::this_thread::sleep_for(std::chrono::milliseconds(250));
- // disconnect if no one is listening
- if (m_numSinksEnabled == 0) {
+ // disconnect if not enabled
+ if (!IsEnabled()) {
std::unique_lock lock(m_mutex);
if (m_streamConn) m_streamConn->stream->close();
- // Wait for a sink to enable
- m_sinkEnabledCond.wait(
- lock, [=] { return !m_active || m_numSinksEnabled != 0; });
+ // Wait for enable
+ m_sinkEnabledCond.wait(lock, [=] { return !m_active || IsEnabled(); });
if (!m_active) return;
}
@@ -185,8 +184,8 @@ void HttpCameraImpl::DeviceStream(wpi::raw_istream& is,
int numErrors = 0;
// streaming loop
- while (m_active && !is.has_error() && m_numSinksEnabled > 0 &&
- numErrors < 3 && !m_streamSettingsUpdated) {
+ while (m_active && !is.has_error() && IsEnabled() && numErrors < 3 &&
+ !m_streamSettingsUpdated) {
if (!FindMultipartBoundary(is, boundary, nullptr)) break;
// Read the next two characters after the boundary (normally \r\n)
diff --git a/cscore/src/main/native/cpp/SourceImpl.h b/cscore/src/main/native/cpp/SourceImpl.h
index a6b50101ac..0eef6c2ca7 100644
--- a/cscore/src/main/native/cpp/SourceImpl.h
+++ b/cscore/src/main/native/cpp/SourceImpl.h
@@ -41,6 +41,15 @@ class SourceImpl : public PropertyContainer {
void SetDescription(const wpi::Twine& description);
wpi::StringRef GetDescription(wpi::SmallVectorImpl& buf) const;
+ void SetConnectionStrategy(CS_ConnectionStrategy strategy) {
+ m_strategy = static_cast(strategy);
+ }
+ bool IsEnabled() const {
+ return m_strategy == CS_CONNECTION_KEEP_OPEN ||
+ (m_strategy == CS_CONNECTION_AUTO_MANAGE && m_numSinksEnabled > 0);
+ }
+
+ // User-visible connection status
void SetConnected(bool connected);
bool IsConnected() const { return m_connected; }
@@ -130,7 +139,6 @@ class SourceImpl : public PropertyContainer {
virtual void NumSinksEnabledChanged() = 0;
std::atomic_int m_numSinks{0};
- std::atomic_int m_numSinksEnabled{0};
protected:
// Cached video modes (protected with m_mutex)
@@ -146,6 +154,9 @@ class SourceImpl : public PropertyContainer {
std::string m_name;
std::string m_description;
+ std::atomic_int m_strategy{CS_CONNECTION_AUTO_MANAGE};
+ std::atomic_int m_numSinksEnabled{0};
+
wpi::mutex m_frameMutex;
wpi::condition_variable m_frameCv;
diff --git a/cscore/src/main/native/cpp/UsbCameraImpl.cpp b/cscore/src/main/native/cpp/UsbCameraImpl.cpp
index c48b421b65..4d7363f79a 100644
--- a/cscore/src/main/native/cpp/UsbCameraImpl.cpp
+++ b/cscore/src/main/native/cpp/UsbCameraImpl.cpp
@@ -309,10 +309,10 @@ void UsbCameraImpl::CameraThreadMain() {
}
}
- // Turn off streaming if no one is listening, and turn it on if there is.
- if (m_streaming && m_numSinksEnabled == 0) {
+ // Turn off streaming if not enabled, and turn it on if enabled
+ if (m_streaming && !IsEnabled()) {
DeviceStreamOff();
- } else if (!m_streaming && m_numSinksEnabled > 0) {
+ } else if (!m_streaming && IsEnabled()) {
DeviceStreamOn();
}
diff --git a/cscore/src/main/native/cpp/cscore_c.cpp b/cscore/src/main/native/cpp/cscore_c.cpp
index 7abfcb5aa4..9ed8aaad22 100644
--- a/cscore/src/main/native/cpp/cscore_c.cpp
+++ b/cscore/src/main/native/cpp/cscore_c.cpp
@@ -99,10 +99,20 @@ uint64_t CS_GetSourceLastFrameTime(CS_Source source, CS_Status* status) {
return cs::GetSourceLastFrameTime(source, status);
}
+void CS_SetSourceConnectionStrategy(CS_Source source,
+ CS_ConnectionStrategy strategy,
+ CS_Status* status) {
+ cs::SetSourceConnectionStrategy(source, strategy, status);
+}
+
CS_Bool CS_IsSourceConnected(CS_Source source, CS_Status* status) {
return cs::IsSourceConnected(source, status);
}
+CS_Bool CS_IsSourceEnabled(CS_Source source, CS_Status* status) {
+ return cs::IsSourceEnabled(source, status);
+}
+
CS_Property CS_GetSourceProperty(CS_Source source, const char* name,
CS_Status* status) {
return cs::GetSourceProperty(source, name, status);
diff --git a/cscore/src/main/native/cpp/cscore_cpp.cpp b/cscore/src/main/native/cpp/cscore_cpp.cpp
index 52f9264b18..b8e699cae3 100644
--- a/cscore/src/main/native/cpp/cscore_cpp.cpp
+++ b/cscore/src/main/native/cpp/cscore_cpp.cpp
@@ -222,6 +222,17 @@ uint64_t GetSourceLastFrameTime(CS_Source source, CS_Status* status) {
return data->source->GetCurFrameTime();
}
+void SetSourceConnectionStrategy(CS_Source source,
+ CS_ConnectionStrategy strategy,
+ CS_Status* status) {
+ auto data = Sources::GetInstance().Get(source);
+ if (!data) {
+ *status = CS_INVALID_HANDLE;
+ return;
+ }
+ data->source->SetConnectionStrategy(strategy);
+}
+
bool IsSourceConnected(CS_Source source, CS_Status* status) {
auto data = Sources::GetInstance().Get(source);
if (!data) {
@@ -231,6 +242,15 @@ bool IsSourceConnected(CS_Source source, CS_Status* status) {
return data->source->IsConnected();
}
+bool IsSourceEnabled(CS_Source source, CS_Status* status) {
+ auto data = Sources::GetInstance().Get(source);
+ if (!data) {
+ *status = CS_INVALID_HANDLE;
+ return false;
+ }
+ return data->source->IsEnabled();
+}
+
CS_Property GetSourceProperty(CS_Source source, const wpi::Twine& name,
CS_Status* status) {
auto data = Sources::GetInstance().Get(source);
diff --git a/cscore/src/main/native/cpp/jni/CameraServerJNI.cpp b/cscore/src/main/native/cpp/jni/CameraServerJNI.cpp
index 49e091998c..128ca0059a 100644
--- a/cscore/src/main/native/cpp/jni/CameraServerJNI.cpp
+++ b/cscore/src/main/native/cpp/jni/CameraServerJNI.cpp
@@ -599,6 +599,21 @@ Java_edu_wpi_cscore_CameraServerJNI_getSourceLastFrameTime
return val;
}
+/*
+ * Class: edu_wpi_cscore_CameraServerJNI
+ * Method: setSourceConnectionStrategy
+ * Signature: (II)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_cscore_CameraServerJNI_setSourceConnectionStrategy
+ (JNIEnv* env, jclass, jint source, jint strategy)
+{
+ CS_Status status = 0;
+ cs::SetSourceConnectionStrategy(
+ source, static_cast(strategy), &status);
+ CheckStatus(env, status);
+}
+
/*
* Class: edu_wpi_cscore_CameraServerJNI
* Method: isSourceConnected
@@ -614,6 +629,21 @@ Java_edu_wpi_cscore_CameraServerJNI_isSourceConnected
return val;
}
+/*
+ * Class: edu_wpi_cscore_CameraServerJNI
+ * Method: isSourceEnabled
+ * Signature: (I)Z
+ */
+JNIEXPORT jboolean JNICALL
+Java_edu_wpi_cscore_CameraServerJNI_isSourceEnabled
+ (JNIEnv* env, jclass, jint source)
+{
+ CS_Status status = 0;
+ auto val = cs::IsSourceEnabled(source, &status);
+ CheckStatus(env, status);
+ return val;
+}
+
/*
* Class: edu_wpi_cscore_CameraServerJNI
* Method: getSourceProperty
diff --git a/cscore/src/main/native/include/cscore_c.h b/cscore/src/main/native/include/cscore_c.h
index 345c011d7a..1163b3e734 100644
--- a/cscore/src/main/native/include/cscore_c.h
+++ b/cscore/src/main/native/include/cscore_c.h
@@ -178,6 +178,27 @@ enum CS_TelemetryKind {
CS_SOURCE_FRAMES_RECEIVED = 2
};
+/** Connection strategy */
+enum CS_ConnectionStrategy {
+ /**
+ * Automatically connect or disconnect based on whether any sinks are
+ * connected to this source. This is the default behavior.
+ */
+ CS_CONNECTION_AUTO_MANAGE = 0,
+
+ /**
+ * Try to keep the connection open regardless of whether any sinks are
+ * connected.
+ */
+ CS_CONNECTION_KEEP_OPEN,
+
+ /**
+ * Never open the connection. If this is set when the connection is open,
+ * close the connection.
+ */
+ CS_CONNECTION_FORCE_CLOSE
+};
+
/**
* Listener event
*/
@@ -245,7 +266,11 @@ enum CS_SourceKind CS_GetSourceKind(CS_Source source, CS_Status* status);
char* CS_GetSourceName(CS_Source source, CS_Status* status);
char* CS_GetSourceDescription(CS_Source source, CS_Status* status);
uint64_t CS_GetSourceLastFrameTime(CS_Source source, CS_Status* status);
+void CS_SetSourceConnectionStrategy(CS_Source source,
+ enum CS_ConnectionStrategy strategy,
+ CS_Status* status);
CS_Bool CS_IsSourceConnected(CS_Source source, CS_Status* status);
+CS_Bool CS_IsSourceEnabled(CS_Source source, CS_Status* status);
CS_Property CS_GetSourceProperty(CS_Source source, const char* name,
CS_Status* status);
CS_Property* CS_EnumerateSourceProperties(CS_Source source, int* count,
diff --git a/cscore/src/main/native/include/cscore_cpp.h b/cscore/src/main/native/include/cscore_cpp.h
index 894a4106c6..a5c345d4de 100644
--- a/cscore/src/main/native/include/cscore_cpp.h
+++ b/cscore/src/main/native/include/cscore_cpp.h
@@ -203,7 +203,11 @@ wpi::StringRef GetSourceDescription(CS_Source source,
wpi::SmallVectorImpl& buf,
CS_Status* status);
uint64_t GetSourceLastFrameTime(CS_Source source, CS_Status* status);
+void SetSourceConnectionStrategy(CS_Source source,
+ CS_ConnectionStrategy strategy,
+ CS_Status* status);
bool IsSourceConnected(CS_Source source, CS_Status* status);
+bool IsSourceEnabled(CS_Source source, CS_Status* status);
CS_Property GetSourceProperty(CS_Source source, const wpi::Twine& name,
CS_Status* status);
wpi::ArrayRef EnumerateSourceProperties(
diff --git a/cscore/src/main/native/include/cscore_oo.h b/cscore/src/main/native/include/cscore_oo.h
index 0eb20507fa..8eec60a0e0 100644
--- a/cscore/src/main/native/include/cscore_oo.h
+++ b/cscore/src/main/native/include/cscore_oo.h
@@ -106,6 +106,27 @@ class VideoSource {
kCv = CS_SOURCE_CV
};
+ /** Connection strategy. Used for SetConnectionStrategy(). */
+ enum ConnectionStrategy {
+ /**
+ * Automatically connect or disconnect based on whether any sinks are
+ * connected to this source. This is the default behavior.
+ */
+ kConnectionAutoManage = CS_CONNECTION_AUTO_MANAGE,
+
+ /**
+ * Try to keep the connection open regardless of whether any sinks are
+ * connected.
+ */
+ kConnectionKeepOpen = CS_CONNECTION_KEEP_OPEN,
+
+ /**
+ * Never open the connection. If this is set when the connection is open,
+ * close the connection.
+ */
+ kConnectionForceClose = CS_CONNECTION_FORCE_CLOSE
+ };
+
VideoSource() noexcept : m_handle(0) {}
VideoSource(const VideoSource& source);
VideoSource(VideoSource&& other) noexcept;
@@ -146,11 +167,30 @@ class VideoSource {
*/
uint64_t GetLastFrameTime() const;
+ /**
+ * Sets the connection strategy. By default, the source will automatically
+ * connect or disconnect based on whether any sinks are connected.
+ *
+ * This function is non-blocking; look for either a connection open or
+ * close event or call IsConnected() to determine the connection state.
+ *
+ * @param strategy connection strategy (auto, keep open, or force close)
+ */
+ void SetConnectionStrategy(ConnectionStrategy strategy);
+
/**
* Is the source currently connected to whatever is providing the images?
*/
bool IsConnected() const;
+ /**
+ * Gets source enable status. This is determined with a combination of
+ * connection strategy and the number of sinks connected.
+ *
+ * @return True if enabled, false otherwise.
+ */
+ bool IsEnabled() const;
+
/** Get a property.
*
* @param name Property name
diff --git a/cscore/src/main/native/include/cscore_oo.inl b/cscore/src/main/native/include/cscore_oo.inl
index 9693045069..74f19b0af8 100644
--- a/cscore/src/main/native/include/cscore_oo.inl
+++ b/cscore/src/main/native/include/cscore_oo.inl
@@ -116,11 +116,23 @@ inline uint64_t VideoSource::GetLastFrameTime() const {
return GetSourceLastFrameTime(m_handle, &m_status);
}
+inline void VideoSource::SetConnectionStrategy(ConnectionStrategy strategy) {
+ m_status = 0;
+ SetSourceConnectionStrategy(
+ m_handle, static_cast(static_cast(strategy)),
+ &m_status);
+}
+
inline bool VideoSource::IsConnected() const {
m_status = 0;
return IsSourceConnected(m_handle, &m_status);
}
+inline bool VideoSource::IsEnabled() const {
+ m_status = 0;
+ return IsSourceEnabled(m_handle, &m_status);
+}
+
inline VideoProperty VideoSource::GetProperty(const wpi::Twine& name) {
m_status = 0;
return VideoProperty{GetSourceProperty(m_handle, name, &m_status)};