cscore: Add config json to VideoSink (#1543)

Same format as VideoSource.

Refactor properties json handling into PropertyContainer.
This commit is contained in:
Peter Johnson
2019-01-11 20:33:05 -08:00
committed by GitHub
parent 1349dd4bd8
commit 05d6660a6b
15 changed files with 317 additions and 54 deletions

View File

@@ -7,6 +7,11 @@
#include "PropertyContainer.h"
#include <wpi/Logger.h>
#include <wpi/SmallString.h>
#include <wpi/SmallVector.h>
#include <wpi/json.h>
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<std::string>();
} 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<std::string>();
WPI_INFO(logger, logName << ": SetConfigJson: setting property '"
<< name << "' to '" << val << '\'');
SetStringProperty(n, val, status);
} else if (v.is_boolean()) {
bool val = v.get<bool>();
WPI_INFO(logger, logName << ": SetConfigJson: setting property '"
<< name << "' to " << val);
SetProperty(n, val, status);
} else {
int val = v.get<int>();
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<int, 32> 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<bool>(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;
}

View File

@@ -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<std::string> 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) {

View File

@@ -7,6 +7,8 @@
#include "SinkImpl.h"
#include <wpi/json.h>
#include "Instance.h"
#include "Notifier.h"
#include "SourceImpl.h"
@@ -102,6 +104,43 @@ wpi::StringRef SinkImpl::GetError(wpi::SmallVectorImpl<char>& 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,

View File

@@ -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<char>& 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;

View File

@@ -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<std::string>();
} 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<std::string>();
SINFO("SetConfigJson: setting property '" << name << "' to '" << val
<< '\'');
SetStringProperty(n, val, status);
} else if (v.is_boolean()) {
bool val = v.get<bool>();
SINFO("SetConfigJson: setting property '" << name << "' to " << val);
SetProperty(n, val, status);
} else {
int val = v.get<int>();
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<int, 32> 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<bool>(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;

View File

@@ -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);
}

View File

@@ -564,6 +564,43 @@ wpi::ArrayRef<CS_Property> 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) {

View File

@@ -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<VideoProperty> VideoSource::EnumerateProperties() const {
wpi::SmallVector<CS_Property, 32> handles_buf;
CS_Status status = 0;

View File

@@ -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