diff --git a/cscore/src/main/java/edu/wpi/cscore/CameraServerJNI.java b/cscore/src/main/java/edu/wpi/cscore/CameraServerJNI.java
index 06f0b4733a..cb6202d7c7 100644
--- a/cscore/src/main/java/edu/wpi/cscore/CameraServerJNI.java
+++ b/cscore/src/main/java/edu/wpi/cscore/CameraServerJNI.java
@@ -147,6 +147,8 @@ public class CameraServerJNI {
public static native String getSinkDescription(int sink);
public static native int getSinkProperty(int sink, String name);
public static native int[] enumerateSinkProperties(int sink);
+ public static native boolean setSinkConfigJson(int sink, String config);
+ public static native String getSinkConfigJson(int sink);
public static native void setSinkSource(int sink, int source);
public static native int getSinkSourceProperty(int sink, String name);
public static native int getSinkSource(int sink);
diff --git a/cscore/src/main/java/edu/wpi/cscore/VideoSink.java b/cscore/src/main/java/edu/wpi/cscore/VideoSink.java
index 1301c9dffa..a59a952b93 100644
--- a/cscore/src/main/java/edu/wpi/cscore/VideoSink.java
+++ b/cscore/src/main/java/edu/wpi/cscore/VideoSink.java
@@ -133,6 +133,38 @@ public class VideoSink implements AutoCloseable {
return rv;
}
+ /**
+ * Set properties from a JSON configuration string.
+ *
+ *
The format of the JSON input is:
+ *
+ *
+ * {
+ * "properties": [
+ * {
+ * "name": property name
+ * "value": property value
+ * }
+ * ]
+ * }
+ *
+ *
+ * @param config configuration
+ * @return True if set successfully
+ */
+ public boolean setConfigJson(String config) {
+ return CameraServerJNI.setSinkConfigJson(m_handle, config);
+ }
+
+ /**
+ * Get a JSON configuration string.
+ *
+ * @return JSON configuration string
+ */
+ public String getConfigJson() {
+ return CameraServerJNI.getSinkConfigJson(m_handle);
+ }
+
/**
* Configure which source should provide frames to this sink. Each sink
* can accept frames from only a single source, but a single source can
diff --git a/cscore/src/main/native/cpp/PropertyContainer.cpp b/cscore/src/main/native/cpp/PropertyContainer.cpp
index d7fae628ab..46ea77dc7d 100644
--- a/cscore/src/main/native/cpp/PropertyContainer.cpp
+++ b/cscore/src/main/native/cpp/PropertyContainer.cpp
@@ -7,6 +7,11 @@
#include "PropertyContainer.h"
+#include
+#include
+#include
+#include
+
using namespace cs;
int PropertyContainer::GetPropertyIndex(const wpi::Twine& name) const {
@@ -204,3 +209,74 @@ bool PropertyContainer::CacheProperties(CS_Status* status) const {
m_properties_cached = true;
return true;
}
+
+bool PropertyContainer::SetPropertiesJson(const wpi::json& config,
+ wpi::Logger& logger,
+ wpi::StringRef logName,
+ CS_Status* status) {
+ for (auto&& prop : config) {
+ std::string name;
+ try {
+ name = prop.at("name").get();
+ } catch (const wpi::json::exception& e) {
+ WPI_WARNING(logger,
+ logName << ": SetConfigJson: could not read property name: "
+ << e.what());
+ continue;
+ }
+ int n = GetPropertyIndex(name);
+ try {
+ auto& v = prop.at("value");
+ if (v.is_string()) {
+ std::string val = v.get();
+ WPI_INFO(logger, logName << ": SetConfigJson: setting property '"
+ << name << "' to '" << val << '\'');
+ SetStringProperty(n, val, status);
+ } else if (v.is_boolean()) {
+ bool val = v.get();
+ WPI_INFO(logger, logName << ": SetConfigJson: setting property '"
+ << name << "' to " << val);
+ SetProperty(n, val, status);
+ } else {
+ int val = v.get();
+ WPI_INFO(logger, logName << ": SetConfigJson: setting property '"
+ << name << "' to " << val);
+ SetProperty(n, val, status);
+ }
+ } catch (const wpi::json::exception& e) {
+ WPI_WARNING(logger,
+ logName << ": SetConfigJson: could not read property value: "
+ << e.what());
+ continue;
+ }
+ }
+
+ return true;
+}
+
+wpi::json PropertyContainer::GetPropertiesJsonObject(CS_Status* status) {
+ wpi::json j;
+ wpi::SmallVector propVec;
+ for (int p : EnumerateProperties(propVec, status)) {
+ wpi::json prop;
+ wpi::SmallString<128> strBuf;
+ prop.emplace("name", GetPropertyName(p, strBuf, status));
+ switch (GetPropertyKind(p)) {
+ case CS_PROP_BOOLEAN:
+ prop.emplace("value", static_cast(GetProperty(p, status)));
+ break;
+ case CS_PROP_INTEGER:
+ case CS_PROP_ENUM:
+ prop.emplace("value", GetProperty(p, status));
+ break;
+ case CS_PROP_STRING:
+ prop.emplace("value", GetStringProperty(p, strBuf, status));
+ break;
+ default:
+ continue;
+ }
+ j.emplace_back(prop);
+ }
+
+ return j;
+}
diff --git a/cscore/src/main/native/cpp/PropertyContainer.h b/cscore/src/main/native/cpp/PropertyContainer.h
index 4aeaf11152..19197b2659 100644
--- a/cscore/src/main/native/cpp/PropertyContainer.h
+++ b/cscore/src/main/native/cpp/PropertyContainer.h
@@ -24,6 +24,11 @@
#include "PropertyImpl.h"
#include "cscore_cpp.h"
+namespace wpi {
+class Logger;
+class json;
+} // namespace wpi
+
namespace cs {
class PropertyContainer {
@@ -50,6 +55,10 @@ class PropertyContainer {
std::vector GetEnumPropertyChoices(int property,
CS_Status* status) const;
+ bool SetPropertiesJson(const wpi::json& config, wpi::Logger& logger,
+ wpi::StringRef logName, CS_Status* status);
+ wpi::json GetPropertiesJsonObject(CS_Status* status);
+
protected:
// Get a property; must be called with m_mutex held.
PropertyImpl* GetProperty(int property) {
diff --git a/cscore/src/main/native/cpp/SinkImpl.cpp b/cscore/src/main/native/cpp/SinkImpl.cpp
index 2ee057549a..637b407f28 100644
--- a/cscore/src/main/native/cpp/SinkImpl.cpp
+++ b/cscore/src/main/native/cpp/SinkImpl.cpp
@@ -7,6 +7,8 @@
#include "SinkImpl.h"
+#include
+
#include "Instance.h"
#include "Notifier.h"
#include "SourceImpl.h"
@@ -102,6 +104,43 @@ wpi::StringRef SinkImpl::GetError(wpi::SmallVectorImpl& buf) const {
return wpi::StringRef{buf.data(), buf.size()};
}
+bool SinkImpl::SetConfigJson(wpi::StringRef config, CS_Status* status) {
+ wpi::json j;
+ try {
+ j = wpi::json::parse(config);
+ } catch (const wpi::json::parse_error& e) {
+ SWARNING("SetConfigJson: parse error at byte " << e.byte << ": "
+ << e.what());
+ *status = CS_PROPERTY_WRITE_FAILED;
+ return false;
+ }
+ return SetConfigJson(j, status);
+}
+
+bool SinkImpl::SetConfigJson(const wpi::json& config, CS_Status* status) {
+ if (config.count("properties") != 0)
+ SetPropertiesJson(config.at("properties"), m_logger, GetName(), status);
+
+ return true;
+}
+
+std::string SinkImpl::GetConfigJson(CS_Status* status) {
+ std::string rv;
+ wpi::raw_string_ostream os(rv);
+ GetConfigJsonObject(status).dump(os, 4);
+ os.flush();
+ return rv;
+}
+
+wpi::json SinkImpl::GetConfigJsonObject(CS_Status* status) {
+ wpi::json j;
+
+ wpi::json props = GetPropertiesJsonObject(status);
+ if (props.is_array()) j.emplace("properties", props);
+
+ return j;
+}
+
void SinkImpl::NotifyPropertyCreated(int propIndex, PropertyImpl& prop) {
m_notifier.NotifySinkProperty(*this, CS_SINK_PROPERTY_CREATED, prop.name,
propIndex, prop.propKind, prop.value,
diff --git a/cscore/src/main/native/cpp/SinkImpl.h b/cscore/src/main/native/cpp/SinkImpl.h
index c51a7950b6..147268fe99 100644
--- a/cscore/src/main/native/cpp/SinkImpl.h
+++ b/cscore/src/main/native/cpp/SinkImpl.h
@@ -18,6 +18,10 @@
#include "SourceImpl.h"
+namespace wpi {
+class json;
+} // namespace wpi
+
namespace cs {
class Frame;
@@ -51,6 +55,11 @@ class SinkImpl : public PropertyContainer {
std::string GetError() const;
wpi::StringRef GetError(wpi::SmallVectorImpl& buf) const;
+ bool SetConfigJson(wpi::StringRef config, CS_Status* status);
+ virtual bool SetConfigJson(const wpi::json& config, CS_Status* status);
+ std::string GetConfigJson(CS_Status* status);
+ virtual wpi::json GetConfigJsonObject(CS_Status* status);
+
protected:
// PropertyContainer implementation
void NotifyPropertyCreated(int propIndex, PropertyImpl& prop) override;
diff --git a/cscore/src/main/native/cpp/SourceImpl.cpp b/cscore/src/main/native/cpp/SourceImpl.cpp
index 01b6e0f17a..e646eb6187 100644
--- a/cscore/src/main/native/cpp/SourceImpl.cpp
+++ b/cscore/src/main/native/cpp/SourceImpl.cpp
@@ -319,38 +319,8 @@ bool SourceImpl::SetConfigJson(const wpi::json& config, CS_Status* status) {
}
// properties
- if (config.count("properties") != 0) {
- for (auto&& prop : config.at("properties")) {
- std::string name;
- try {
- name = prop.at("name").get();
- } catch (const wpi::json::exception& e) {
- SWARNING("SetConfigJson: could not read property name: " << e.what());
- continue;
- }
- int n = GetPropertyIndex(name);
- try {
- auto& v = prop.at("value");
- if (v.is_string()) {
- std::string val = v.get();
- SINFO("SetConfigJson: setting property '" << name << "' to '" << val
- << '\'');
- SetStringProperty(n, val, status);
- } else if (v.is_boolean()) {
- bool val = v.get();
- SINFO("SetConfigJson: setting property '" << name << "' to " << val);
- SetProperty(n, val, status);
- } else {
- int val = v.get();
- SINFO("SetConfigJson: setting property '" << name << "' to " << val);
- SetProperty(n, val, status);
- }
- } catch (const wpi::json::exception& e) {
- SWARNING("SetConfigJson: could not read property value: " << e.what());
- continue;
- }
- }
- }
+ if (config.count("properties") != 0)
+ SetPropertiesJson(config.at("properties"), m_logger, GetName(), status);
return true;
}
@@ -401,28 +371,7 @@ wpi::json SourceImpl::GetConfigJsonObject(CS_Status* status) {
// TODO: output brightness, white balance, and exposure?
// properties
- wpi::json props;
- wpi::SmallVector propVec;
- for (int p : EnumerateProperties(propVec, status)) {
- wpi::json prop;
- wpi::SmallString<128> strBuf;
- prop.emplace("name", GetPropertyName(p, strBuf, status));
- switch (GetPropertyKind(p)) {
- case CS_PROP_BOOLEAN:
- prop.emplace("value", static_cast(GetProperty(p, status)));
- break;
- case CS_PROP_INTEGER:
- case CS_PROP_ENUM:
- prop.emplace("value", GetProperty(p, status));
- break;
- case CS_PROP_STRING:
- prop.emplace("value", GetStringProperty(p, strBuf, status));
- break;
- default:
- continue;
- }
- props.emplace_back(prop);
- }
+ wpi::json props = GetPropertiesJsonObject(status);
if (props.is_array()) j.emplace("properties", props);
return j;
diff --git a/cscore/src/main/native/cpp/cscore_c.cpp b/cscore/src/main/native/cpp/cscore_c.cpp
index 26d2c71075..1819d13835 100644
--- a/cscore/src/main/native/cpp/cscore_c.cpp
+++ b/cscore/src/main/native/cpp/cscore_c.cpp
@@ -277,6 +277,15 @@ CS_Property* CS_EnumerateSinkProperties(CS_Sink sink, int* count,
return out;
}
+CS_Bool CS_SetSinkConfigJson(CS_Sink sink, const char* config,
+ CS_Status* status) {
+ return cs::SetSinkConfigJson(sink, config, status);
+}
+
+char* CS_GetSinkConfigJson(CS_Sink sink, CS_Status* status) {
+ return cs::ConvertToC(cs::GetSinkConfigJson(sink, status));
+}
+
void CS_SetSinkSource(CS_Sink sink, CS_Source source, CS_Status* status) {
return cs::SetSinkSource(sink, source, status);
}
diff --git a/cscore/src/main/native/cpp/cscore_cpp.cpp b/cscore/src/main/native/cpp/cscore_cpp.cpp
index c40e9e26c0..83b4e8779c 100644
--- a/cscore/src/main/native/cpp/cscore_cpp.cpp
+++ b/cscore/src/main/native/cpp/cscore_cpp.cpp
@@ -564,6 +564,43 @@ wpi::ArrayRef EnumerateSinkProperties(
return vec;
}
+bool SetSinkConfigJson(CS_Sink sink, wpi::StringRef config, CS_Status* status) {
+ auto data = Instance::GetInstance().GetSink(sink);
+ if (!data) {
+ *status = CS_INVALID_HANDLE;
+ return false;
+ }
+ return data->sink->SetConfigJson(config, status);
+}
+
+bool SetSinkConfigJson(CS_Sink sink, const wpi::json& config,
+ CS_Status* status) {
+ auto data = Instance::GetInstance().GetSink(sink);
+ if (!data) {
+ *status = CS_INVALID_HANDLE;
+ return false;
+ }
+ return data->sink->SetConfigJson(config, status);
+}
+
+std::string GetSinkConfigJson(CS_Sink sink, CS_Status* status) {
+ auto data = Instance::GetInstance().GetSink(sink);
+ if (!data) {
+ *status = CS_INVALID_HANDLE;
+ return std::string{};
+ }
+ return data->sink->GetConfigJson(status);
+}
+
+wpi::json GetSinkConfigJsonObject(CS_Sink sink, CS_Status* status) {
+ auto data = Instance::GetInstance().GetSink(sink);
+ if (!data) {
+ *status = CS_INVALID_HANDLE;
+ return wpi::json{};
+ }
+ return data->sink->GetConfigJsonObject(status);
+}
+
void SetSinkSource(CS_Sink sink, CS_Source source, CS_Status* status) {
auto data = Instance::GetInstance().GetSink(sink);
if (!data) {
diff --git a/cscore/src/main/native/cpp/cscore_oo.cpp b/cscore/src/main/native/cpp/cscore_oo.cpp
index cbb6b148ba..f455273eaa 100644
--- a/cscore/src/main/native/cpp/cscore_oo.cpp
+++ b/cscore/src/main/native/cpp/cscore_oo.cpp
@@ -16,6 +16,11 @@ wpi::json VideoSource::GetConfigJsonObject() const {
return GetSourceConfigJsonObject(m_handle, &m_status);
}
+wpi::json VideoSink::GetConfigJsonObject() const {
+ m_status = 0;
+ return GetSinkConfigJsonObject(m_handle, &m_status);
+}
+
std::vector VideoSource::EnumerateProperties() const {
wpi::SmallVector handles_buf;
CS_Status status = 0;
diff --git a/cscore/src/main/native/cpp/jni/CameraServerJNI.cpp b/cscore/src/main/native/cpp/jni/CameraServerJNI.cpp
index fa455894d0..528d5e4110 100644
--- a/cscore/src/main/native/cpp/jni/CameraServerJNI.cpp
+++ b/cscore/src/main/native/cpp/jni/CameraServerJNI.cpp
@@ -1292,6 +1292,36 @@ Java_edu_wpi_cscore_CameraServerJNI_enumerateSinkProperties
return MakeJIntArray(env, arr);
}
+/*
+ * Class: edu_wpi_cscore_CameraServerJNI
+ * Method: setSinkConfigJson
+ * Signature: (ILjava/lang/String;)Z
+ */
+JNIEXPORT jboolean JNICALL
+Java_edu_wpi_cscore_CameraServerJNI_setSinkConfigJson
+ (JNIEnv* env, jclass, jint source, jstring config)
+{
+ CS_Status status = 0;
+ auto val = cs::SetSinkConfigJson(source, JStringRef{env, config}, &status);
+ CheckStatus(env, status);
+ return val;
+}
+
+/*
+ * Class: edu_wpi_cscore_CameraServerJNI
+ * Method: getSinkConfigJson
+ * Signature: (I)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL
+Java_edu_wpi_cscore_CameraServerJNI_getSinkConfigJson
+ (JNIEnv* env, jclass, jint source)
+{
+ CS_Status status = 0;
+ auto val = cs::GetSinkConfigJson(source, &status);
+ CheckStatus(env, status);
+ return MakeJString(env, val);
+}
+
/*
* Class: edu_wpi_cscore_CameraServerJNI
* Method: setSinkSource
diff --git a/cscore/src/main/native/include/cscore_c.h b/cscore/src/main/native/include/cscore_c.h
index 0cc354bb6a..182b9b2df9 100644
--- a/cscore/src/main/native/include/cscore_c.h
+++ b/cscore/src/main/native/include/cscore_c.h
@@ -393,6 +393,9 @@ CS_Property* CS_EnumerateSinkProperties(CS_Sink sink, int* count,
void CS_SetSinkSource(CS_Sink sink, CS_Source source, CS_Status* status);
CS_Property CS_GetSinkSourceProperty(CS_Sink sink, const char* name,
CS_Status* status);
+CS_Bool CS_SetSinkConfigJson(CS_Sink sink, const char* config,
+ CS_Status* status);
+char* CS_GetSinkConfigJson(CS_Sink sink, CS_Status* status);
CS_Source CS_GetSinkSource(CS_Sink sink, CS_Status* status);
CS_Sink CS_CopySink(CS_Sink sink, CS_Status* status);
void CS_ReleaseSink(CS_Sink sink, CS_Status* status);
diff --git a/cscore/src/main/native/include/cscore_cpp.h b/cscore/src/main/native/include/cscore_cpp.h
index 040b726fd2..a400b783b2 100644
--- a/cscore/src/main/native/include/cscore_cpp.h
+++ b/cscore/src/main/native/include/cscore_cpp.h
@@ -332,6 +332,11 @@ wpi::ArrayRef EnumerateSinkProperties(
void SetSinkSource(CS_Sink sink, CS_Source source, CS_Status* status);
CS_Property GetSinkSourceProperty(CS_Sink sink, const wpi::Twine& name,
CS_Status* status);
+bool SetSinkConfigJson(CS_Sink sink, wpi::StringRef config, CS_Status* status);
+bool SetSinkConfigJson(CS_Sink sink, const wpi::json& config,
+ CS_Status* status);
+std::string GetSinkConfigJson(CS_Sink sink, CS_Status* status);
+wpi::json GetSinkConfigJsonObject(CS_Sink sink, CS_Status* status);
CS_Source GetSinkSource(CS_Sink sink, CS_Status* status);
CS_Sink CopySink(CS_Sink sink, CS_Status* status);
void ReleaseSink(CS_Sink sink, CS_Status* status);
diff --git a/cscore/src/main/native/include/cscore_oo.h b/cscore/src/main/native/include/cscore_oo.h
index 9a8b3a0c1f..7e0cb9f43c 100644
--- a/cscore/src/main/native/include/cscore_oo.h
+++ b/cscore/src/main/native/include/cscore_oo.h
@@ -807,6 +807,49 @@ class VideoSink {
*/
std::vector EnumerateProperties() const;
+ /**
+ * Set properties from a JSON configuration string.
+ *
+ * The format of the JSON input is:
+ *
+ *
+ * {
+ * "properties": [
+ * {
+ * "name": property name
+ * "value": property value
+ * }
+ * ]
+ * }
+ *
+ *
+ * @param config configuration
+ * @return True if set successfully
+ */
+ bool SetConfigJson(wpi::StringRef config);
+
+ /**
+ * Set properties from a JSON configuration object.
+ *
+ * @param config configuration
+ * @return True if set successfully
+ */
+ bool SetConfigJson(const wpi::json& config);
+
+ /**
+ * Get a JSON configuration string.
+ *
+ * @return JSON configuration string
+ */
+ std::string GetConfigJson() const;
+
+ /**
+ * Get a JSON configuration object.
+ *
+ * @return JSON configuration object
+ */
+ wpi::json GetConfigJsonObject() const;
+
/**
* Configure which source should provide frames to this sink. Each sink
* can accept frames from only a single source, but a single source can
diff --git a/cscore/src/main/native/include/cscore_oo.inl b/cscore/src/main/native/include/cscore_oo.inl
index 1767452fae..5162e1db88 100644
--- a/cscore/src/main/native/include/cscore_oo.inl
+++ b/cscore/src/main/native/include/cscore_oo.inl
@@ -521,6 +521,21 @@ inline VideoProperty VideoSink::GetSourceProperty(const wpi::Twine& name) {
return VideoProperty{GetSinkSourceProperty(m_handle, name, &m_status)};
}
+inline bool VideoSink::SetConfigJson(wpi::StringRef config) {
+ m_status = 0;
+ return SetSinkConfigJson(m_handle, config, &m_status);
+}
+
+inline bool VideoSink::SetConfigJson(const wpi::json& config) {
+ m_status = 0;
+ return SetSinkConfigJson(m_handle, config, &m_status);
+}
+
+inline std::string VideoSink::GetConfigJson() const {
+ m_status = 0;
+ return GetSinkConfigJson(m_handle, &m_status);
+}
+
inline MjpegServer::MjpegServer(const wpi::Twine& name,
const wpi::Twine& listenAddress, int port) {
m_handle = CreateMjpegServer(name, listenAddress, port, &m_status);