mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-19 00:41:43 +00:00
cscore: Add properties support to VideoSink (#1228)
Refactor the Property internals into PropertyContainer.
This commit is contained in:
@@ -206,6 +206,8 @@ public class CameraServerJNI {
|
||||
public static native int getSinkKind(int sink);
|
||||
public static native String getSinkName(int sink);
|
||||
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 void setSinkSource(int sink, int source);
|
||||
public static native int getSinkSourceProperty(int sink, String name);
|
||||
public static native int getSinkSource(int sink);
|
||||
|
||||
@@ -28,7 +28,10 @@ public class VideoEvent {
|
||||
kSinkEnabled(0x1000),
|
||||
kSinkDisabled(0x2000),
|
||||
kNetworkInterfacesChanged(0x4000),
|
||||
kTelemetryUpdated(0x8000);
|
||||
kTelemetryUpdated(0x8000),
|
||||
kSinkPropertyCreated(0x10000),
|
||||
kSinkPropertyValueUpdated(0x20000),
|
||||
kSinkPropertyChoicesUpdated(0x40000);
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
private int value;
|
||||
@@ -66,6 +69,9 @@ public class VideoEvent {
|
||||
case 0x1000: return Kind.kSinkEnabled;
|
||||
case 0x2000: return Kind.kSinkDisabled;
|
||||
case 0x4000: return Kind.kNetworkInterfacesChanged;
|
||||
case 0x10000: return Kind.kSinkPropertyCreated;
|
||||
case 0x20000: return Kind.kSinkPropertyValueUpdated;
|
||||
case 0x40000: return Kind.kSinkPropertyChoicesUpdated;
|
||||
default: return Kind.kUnknown;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,6 +109,29 @@ public class VideoSink implements AutoCloseable {
|
||||
return CameraServerJNI.getSinkDescription(m_handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a property of the sink.
|
||||
* @param name Property name
|
||||
* @return Property (kind Property::kNone if no property with
|
||||
* the given name exists)
|
||||
*/
|
||||
public VideoProperty getProperty(String name) {
|
||||
return new VideoProperty(CameraServerJNI.getSinkProperty(m_handle, name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerate all properties of this sink.
|
||||
*/
|
||||
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
|
||||
public VideoProperty[] enumerateProperties() {
|
||||
int[] handles = CameraServerJNI.enumerateSinkProperties(m_handle);
|
||||
VideoProperty[] rv = new VideoProperty[handles.length];
|
||||
for (int i = 0; i < handles.length; i++) {
|
||||
rv[i] = new VideoProperty(handles[i]);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure which source should provide frames to this sink. Each sink
|
||||
* can accept frames from only a single source, but a single source can
|
||||
|
||||
@@ -31,57 +31,6 @@ CvSourceImpl::~CvSourceImpl() {}
|
||||
|
||||
void CvSourceImpl::Start() {}
|
||||
|
||||
std::unique_ptr<PropertyImpl> CvSourceImpl::CreateEmptyProperty(
|
||||
wpi::StringRef name) const {
|
||||
return wpi::make_unique<PropertyData>(name);
|
||||
}
|
||||
|
||||
bool CvSourceImpl::CacheProperties(CS_Status* status) const {
|
||||
// Doesn't need to do anything.
|
||||
m_properties_cached = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void CvSourceImpl::SetProperty(int property, int value, CS_Status* status) {
|
||||
std::lock_guard<wpi::mutex> lock(m_mutex);
|
||||
auto prop = static_cast<PropertyData*>(GetProperty(property));
|
||||
if (!prop) {
|
||||
*status = CS_INVALID_PROPERTY;
|
||||
return;
|
||||
}
|
||||
|
||||
// Guess it's integer if we've set before get
|
||||
if (prop->propKind == CS_PROP_NONE) prop->propKind = CS_PROP_INTEGER;
|
||||
|
||||
if ((prop->propKind & (CS_PROP_BOOLEAN | CS_PROP_INTEGER | CS_PROP_ENUM)) ==
|
||||
0) {
|
||||
*status = CS_WRONG_PROPERTY_TYPE;
|
||||
return;
|
||||
}
|
||||
|
||||
UpdatePropertyValue(property, false, value, wpi::StringRef{});
|
||||
}
|
||||
|
||||
void CvSourceImpl::SetStringProperty(int property, wpi::StringRef value,
|
||||
CS_Status* status) {
|
||||
std::lock_guard<wpi::mutex> lock(m_mutex);
|
||||
auto prop = static_cast<PropertyData*>(GetProperty(property));
|
||||
if (!prop) {
|
||||
*status = CS_INVALID_PROPERTY;
|
||||
return;
|
||||
}
|
||||
|
||||
// Guess it's string if we've set before get
|
||||
if (prop->propKind == CS_PROP_NONE) prop->propKind = CS_PROP_STRING;
|
||||
|
||||
if (prop->propKind != CS_PROP_STRING) {
|
||||
*status = CS_WRONG_PROPERTY_TYPE;
|
||||
return;
|
||||
}
|
||||
|
||||
UpdatePropertyValue(property, true, 0, value);
|
||||
}
|
||||
|
||||
// These are only valid for cameras (should never get called)
|
||||
|
||||
void CvSourceImpl::SetBrightness(int brightness, CS_Status* status) {
|
||||
@@ -176,22 +125,21 @@ int CvSourceImpl::CreateProperty(wpi::StringRef name, CS_PropertyKind kind,
|
||||
int minimum, int maximum, int step,
|
||||
int defaultValue, int value) {
|
||||
std::lock_guard<wpi::mutex> lock(m_mutex);
|
||||
int& ndx = m_properties[name];
|
||||
if (ndx == 0) {
|
||||
// create a new index
|
||||
ndx = m_propertyData.size() + 1;
|
||||
m_propertyData.emplace_back(wpi::make_unique<PropertyData>(
|
||||
name, kind, minimum, maximum, step, defaultValue, value));
|
||||
} else {
|
||||
// update all but value
|
||||
auto prop = GetProperty(ndx);
|
||||
prop->propKind = kind;
|
||||
prop->minimum = minimum;
|
||||
prop->maximum = maximum;
|
||||
prop->step = step;
|
||||
prop->defaultValue = defaultValue;
|
||||
value = prop->value;
|
||||
}
|
||||
int ndx = CreateOrUpdateProperty(name,
|
||||
[=] {
|
||||
return wpi::make_unique<PropertyImpl>(
|
||||
name, kind, minimum, maximum, step,
|
||||
defaultValue, value);
|
||||
},
|
||||
[&](PropertyImpl& prop) {
|
||||
// update all but value
|
||||
prop.propKind = kind;
|
||||
prop.minimum = minimum;
|
||||
prop.maximum = maximum;
|
||||
prop.step = step;
|
||||
prop.defaultValue = defaultValue;
|
||||
value = prop.value;
|
||||
});
|
||||
Notifier::GetInstance().NotifySourceProperty(
|
||||
*this, CS_SOURCE_PROPERTY_CREATED, name, ndx, kind, value,
|
||||
wpi::StringRef{});
|
||||
|
||||
@@ -25,11 +25,6 @@ class CvSourceImpl : public SourceImpl {
|
||||
|
||||
void Start();
|
||||
|
||||
// Property functions
|
||||
void SetProperty(int property, int value, CS_Status* status) override;
|
||||
void SetStringProperty(int property, wpi::StringRef value,
|
||||
CS_Status* status) override;
|
||||
|
||||
// Standard common camera properties
|
||||
void SetBrightness(int brightness, CS_Status* status) override;
|
||||
int GetBrightness(CS_Status* status) const override;
|
||||
@@ -56,30 +51,6 @@ class CvSourceImpl : public SourceImpl {
|
||||
void SetEnumPropertyChoices(int property, wpi::ArrayRef<std::string> choices,
|
||||
CS_Status* status);
|
||||
|
||||
// Property data
|
||||
class PropertyData : public PropertyImpl {
|
||||
public:
|
||||
PropertyData() = default;
|
||||
explicit PropertyData(wpi::StringRef name_) : PropertyImpl{name_} {}
|
||||
PropertyData(wpi::StringRef name_, CS_PropertyKind kind_, int minimum_,
|
||||
int maximum_, int step_, int defaultValue_, int value_)
|
||||
: PropertyImpl{name_, kind_, step_, defaultValue_, value_} {
|
||||
hasMinimum = true;
|
||||
minimum = minimum_;
|
||||
hasMaximum = true;
|
||||
maximum = maximum_;
|
||||
}
|
||||
~PropertyData() override = default;
|
||||
|
||||
std::function<void(CS_Property property)> onChange;
|
||||
};
|
||||
|
||||
protected:
|
||||
std::unique_ptr<PropertyImpl> CreateEmptyProperty(
|
||||
wpi::StringRef name) const override;
|
||||
|
||||
bool CacheProperties(CS_Status* status) const override;
|
||||
|
||||
private:
|
||||
std::atomic_bool m_connected{true};
|
||||
};
|
||||
|
||||
@@ -29,7 +29,14 @@ class SourceImpl;
|
||||
|
||||
class Handle {
|
||||
public:
|
||||
enum Type { kUndefined = 0, kProperty = 0x40, kSource, kSink, kListener };
|
||||
enum Type {
|
||||
kUndefined = 0,
|
||||
kProperty = 0x40,
|
||||
kSource,
|
||||
kSink,
|
||||
kListener,
|
||||
kSinkProperty
|
||||
};
|
||||
enum { kIndexMax = 0xffff };
|
||||
|
||||
Handle(CS_Handle handle) : m_handle(handle) {} // NOLINT
|
||||
@@ -58,8 +65,9 @@ class Handle {
|
||||
bool IsType(Type type) const { return type == GetType(); }
|
||||
int GetTypedIndex(Type type) const { return IsType(type) ? GetIndex() : -1; }
|
||||
int GetParentIndex() const {
|
||||
return IsType(Handle::kProperty) ? (static_cast<int>(m_handle) >> 16) & 0xff
|
||||
: -1;
|
||||
return (IsType(Handle::kProperty) || IsType(Handle::kSinkProperty))
|
||||
? (static_cast<int>(m_handle) >> 16) & 0xff
|
||||
: -1;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -217,6 +217,22 @@ void Notifier::NotifySinkSourceChanged(wpi::StringRef name, CS_Sink sink,
|
||||
thr->m_cond.notify_one();
|
||||
}
|
||||
|
||||
void Notifier::NotifySinkProperty(const SinkImpl& sink, CS_EventKind kind,
|
||||
wpi::StringRef propertyName, int property,
|
||||
CS_PropertyKind propertyKind, int value,
|
||||
wpi::StringRef valueStr) {
|
||||
auto thr = m_owner.GetThread();
|
||||
if (!thr) return;
|
||||
|
||||
auto handleData = Sinks::GetInstance().Find(sink);
|
||||
|
||||
thr->m_notifications.emplace(
|
||||
propertyName, handleData.first, static_cast<RawEvent::Kind>(kind),
|
||||
Handle{handleData.first, property, Handle::kSinkProperty}, propertyKind,
|
||||
value, valueStr);
|
||||
thr->m_cond.notify_one();
|
||||
}
|
||||
|
||||
void Notifier::NotifyNetworkInterfacesChanged() {
|
||||
auto thr = m_owner.GetThread();
|
||||
if (!thr) return;
|
||||
|
||||
@@ -53,6 +53,10 @@ class Notifier {
|
||||
void NotifySink(const SinkImpl& sink, CS_EventKind kind);
|
||||
void NotifySinkSourceChanged(wpi::StringRef name, CS_Sink sink,
|
||||
CS_Source source);
|
||||
void NotifySinkProperty(const SinkImpl& sink, CS_EventKind kind,
|
||||
wpi::StringRef propertyName, int property,
|
||||
CS_PropertyKind propertyKind, int value,
|
||||
wpi::StringRef valueStr);
|
||||
void NotifyNetworkInterfacesChanged();
|
||||
void NotifyTelemetryUpdated();
|
||||
|
||||
|
||||
205
cscore/src/main/native/cpp/PropertyContainer.cpp
Normal file
205
cscore/src/main/native/cpp/PropertyContainer.cpp
Normal file
@@ -0,0 +1,205 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2016-2018 FIRST. 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "PropertyContainer.h"
|
||||
|
||||
using namespace cs;
|
||||
|
||||
int PropertyContainer::GetPropertyIndex(wpi::StringRef name) const {
|
||||
// We can't fail, so instead we create a new index if caching fails.
|
||||
CS_Status status = 0;
|
||||
if (!m_properties_cached) CacheProperties(&status);
|
||||
std::lock_guard<wpi::mutex> lock(m_mutex);
|
||||
int& ndx = m_properties[name];
|
||||
if (ndx == 0) {
|
||||
// create a new index
|
||||
ndx = m_propertyData.size() + 1;
|
||||
m_propertyData.emplace_back(CreateEmptyProperty(name));
|
||||
}
|
||||
return ndx;
|
||||
}
|
||||
|
||||
wpi::ArrayRef<int> PropertyContainer::EnumerateProperties(
|
||||
wpi::SmallVectorImpl<int>& vec, CS_Status* status) const {
|
||||
if (!m_properties_cached && !CacheProperties(status))
|
||||
return wpi::ArrayRef<int>{};
|
||||
std::lock_guard<wpi::mutex> lock(m_mutex);
|
||||
for (int i = 0; i < static_cast<int>(m_propertyData.size()); ++i) {
|
||||
if (m_propertyData[i]) vec.push_back(i + 1);
|
||||
}
|
||||
return vec;
|
||||
}
|
||||
|
||||
CS_PropertyKind PropertyContainer::GetPropertyKind(int property) const {
|
||||
CS_Status status = 0;
|
||||
if (!m_properties_cached && !CacheProperties(&status)) return CS_PROP_NONE;
|
||||
std::lock_guard<wpi::mutex> lock(m_mutex);
|
||||
auto prop = GetProperty(property);
|
||||
if (!prop) return CS_PROP_NONE;
|
||||
return prop->propKind;
|
||||
}
|
||||
|
||||
wpi::StringRef PropertyContainer::GetPropertyName(
|
||||
int property, wpi::SmallVectorImpl<char>& buf, CS_Status* status) const {
|
||||
if (!m_properties_cached && !CacheProperties(status)) return wpi::StringRef{};
|
||||
std::lock_guard<wpi::mutex> lock(m_mutex);
|
||||
auto prop = GetProperty(property);
|
||||
if (!prop) {
|
||||
*status = CS_INVALID_PROPERTY;
|
||||
return wpi::StringRef{};
|
||||
}
|
||||
// safe to not copy because we never modify it after caching
|
||||
return prop->name;
|
||||
}
|
||||
|
||||
int PropertyContainer::GetProperty(int property, CS_Status* status) const {
|
||||
if (!m_properties_cached && !CacheProperties(status)) return 0;
|
||||
std::lock_guard<wpi::mutex> lock(m_mutex);
|
||||
auto prop = GetProperty(property);
|
||||
if (!prop) {
|
||||
*status = CS_INVALID_PROPERTY;
|
||||
return 0;
|
||||
}
|
||||
if ((prop->propKind & (CS_PROP_BOOLEAN | CS_PROP_INTEGER | CS_PROP_ENUM)) ==
|
||||
0) {
|
||||
*status = CS_WRONG_PROPERTY_TYPE;
|
||||
return 0;
|
||||
}
|
||||
return prop->value;
|
||||
}
|
||||
|
||||
void PropertyContainer::SetProperty(int property, int value,
|
||||
CS_Status* status) {
|
||||
std::lock_guard<wpi::mutex> lock(m_mutex);
|
||||
auto prop = GetProperty(property);
|
||||
if (!prop) {
|
||||
*status = CS_INVALID_PROPERTY;
|
||||
return;
|
||||
}
|
||||
|
||||
// Guess it's integer if we've set before get
|
||||
if (prop->propKind == CS_PROP_NONE) prop->propKind = CS_PROP_INTEGER;
|
||||
|
||||
if ((prop->propKind & (CS_PROP_BOOLEAN | CS_PROP_INTEGER | CS_PROP_ENUM)) ==
|
||||
0) {
|
||||
*status = CS_WRONG_PROPERTY_TYPE;
|
||||
return;
|
||||
}
|
||||
|
||||
UpdatePropertyValue(property, false, value, wpi::StringRef{});
|
||||
}
|
||||
|
||||
int PropertyContainer::GetPropertyMin(int property, CS_Status* status) const {
|
||||
if (!m_properties_cached && !CacheProperties(status)) return 0;
|
||||
std::lock_guard<wpi::mutex> lock(m_mutex);
|
||||
auto prop = GetProperty(property);
|
||||
if (!prop) {
|
||||
*status = CS_INVALID_PROPERTY;
|
||||
return 0;
|
||||
}
|
||||
return prop->minimum;
|
||||
}
|
||||
|
||||
int PropertyContainer::GetPropertyMax(int property, CS_Status* status) const {
|
||||
if (!m_properties_cached && !CacheProperties(status)) return 0;
|
||||
std::lock_guard<wpi::mutex> lock(m_mutex);
|
||||
auto prop = GetProperty(property);
|
||||
if (!prop) {
|
||||
*status = CS_INVALID_PROPERTY;
|
||||
return 0;
|
||||
}
|
||||
return prop->maximum;
|
||||
}
|
||||
|
||||
int PropertyContainer::GetPropertyStep(int property, CS_Status* status) const {
|
||||
if (!m_properties_cached && !CacheProperties(status)) return 0;
|
||||
std::lock_guard<wpi::mutex> lock(m_mutex);
|
||||
auto prop = GetProperty(property);
|
||||
if (!prop) {
|
||||
*status = CS_INVALID_PROPERTY;
|
||||
return 0;
|
||||
}
|
||||
return prop->step;
|
||||
}
|
||||
|
||||
int PropertyContainer::GetPropertyDefault(int property,
|
||||
CS_Status* status) const {
|
||||
if (!m_properties_cached && !CacheProperties(status)) return 0;
|
||||
std::lock_guard<wpi::mutex> lock(m_mutex);
|
||||
auto prop = GetProperty(property);
|
||||
if (!prop) {
|
||||
*status = CS_INVALID_PROPERTY;
|
||||
return 0;
|
||||
}
|
||||
return prop->defaultValue;
|
||||
}
|
||||
|
||||
wpi::StringRef PropertyContainer::GetStringProperty(
|
||||
int property, wpi::SmallVectorImpl<char>& buf, CS_Status* status) const {
|
||||
if (!m_properties_cached && !CacheProperties(status)) return wpi::StringRef{};
|
||||
std::lock_guard<wpi::mutex> lock(m_mutex);
|
||||
auto prop = GetProperty(property);
|
||||
if (!prop) {
|
||||
*status = CS_INVALID_PROPERTY;
|
||||
return wpi::StringRef{};
|
||||
}
|
||||
if (prop->propKind != CS_PROP_STRING) {
|
||||
*status = CS_WRONG_PROPERTY_TYPE;
|
||||
return wpi::StringRef{};
|
||||
}
|
||||
buf.clear();
|
||||
buf.append(prop->valueStr.begin(), prop->valueStr.end());
|
||||
return wpi::StringRef(buf.data(), buf.size());
|
||||
}
|
||||
|
||||
void PropertyContainer::SetStringProperty(int property, wpi::StringRef value,
|
||||
CS_Status* status) {
|
||||
std::lock_guard<wpi::mutex> lock(m_mutex);
|
||||
auto prop = GetProperty(property);
|
||||
if (!prop) {
|
||||
*status = CS_INVALID_PROPERTY;
|
||||
return;
|
||||
}
|
||||
|
||||
// Guess it's string if we've set before get
|
||||
if (prop->propKind == CS_PROP_NONE) prop->propKind = CS_PROP_STRING;
|
||||
|
||||
if (prop->propKind != CS_PROP_STRING) {
|
||||
*status = CS_WRONG_PROPERTY_TYPE;
|
||||
return;
|
||||
}
|
||||
|
||||
UpdatePropertyValue(property, true, 0, value);
|
||||
}
|
||||
|
||||
std::vector<std::string> PropertyContainer::GetEnumPropertyChoices(
|
||||
int property, CS_Status* status) const {
|
||||
if (!m_properties_cached && !CacheProperties(status))
|
||||
return std::vector<std::string>{};
|
||||
std::lock_guard<wpi::mutex> lock(m_mutex);
|
||||
auto prop = GetProperty(property);
|
||||
if (!prop) {
|
||||
*status = CS_INVALID_PROPERTY;
|
||||
return std::vector<std::string>{};
|
||||
}
|
||||
if (prop->propKind != CS_PROP_ENUM) {
|
||||
*status = CS_WRONG_PROPERTY_TYPE;
|
||||
return std::vector<std::string>{};
|
||||
}
|
||||
return prop->enumChoices;
|
||||
}
|
||||
|
||||
std::unique_ptr<PropertyImpl> PropertyContainer::CreateEmptyProperty(
|
||||
wpi::StringRef name) const {
|
||||
return wpi::make_unique<PropertyImpl>(name);
|
||||
}
|
||||
|
||||
bool PropertyContainer::CacheProperties(CS_Status* status) const {
|
||||
// Doesn't need to do anything.
|
||||
m_properties_cached = true;
|
||||
return true;
|
||||
}
|
||||
113
cscore/src/main/native/cpp/PropertyContainer.h
Normal file
113
cscore/src/main/native/cpp/PropertyContainer.h
Normal file
@@ -0,0 +1,113 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2016-2018 FIRST. 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef CSCORE_PROPERTYCONTAINER_H_
|
||||
#define CSCORE_PROPERTYCONTAINER_H_
|
||||
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <wpi/ArrayRef.h>
|
||||
#include <wpi/SmallVector.h>
|
||||
#include <wpi/StringMap.h>
|
||||
#include <wpi/StringRef.h>
|
||||
#include <wpi/mutex.h>
|
||||
|
||||
#include "PropertyImpl.h"
|
||||
#include "cscore_cpp.h"
|
||||
|
||||
namespace cs {
|
||||
|
||||
class PropertyContainer {
|
||||
public:
|
||||
virtual ~PropertyContainer() = default;
|
||||
|
||||
int GetPropertyIndex(wpi::StringRef name) const;
|
||||
wpi::ArrayRef<int> EnumerateProperties(wpi::SmallVectorImpl<int>& vec,
|
||||
CS_Status* status) const;
|
||||
CS_PropertyKind GetPropertyKind(int property) const;
|
||||
wpi::StringRef GetPropertyName(int property, wpi::SmallVectorImpl<char>& buf,
|
||||
CS_Status* status) const;
|
||||
int GetProperty(int property, CS_Status* status) const;
|
||||
virtual void SetProperty(int property, int value, CS_Status* status);
|
||||
int GetPropertyMin(int property, CS_Status* status) const;
|
||||
int GetPropertyMax(int property, CS_Status* status) const;
|
||||
int GetPropertyStep(int property, CS_Status* status) const;
|
||||
int GetPropertyDefault(int property, CS_Status* status) const;
|
||||
wpi::StringRef GetStringProperty(int property,
|
||||
wpi::SmallVectorImpl<char>& buf,
|
||||
CS_Status* status) const;
|
||||
virtual void SetStringProperty(int property, wpi::StringRef value,
|
||||
CS_Status* status);
|
||||
std::vector<std::string> GetEnumPropertyChoices(int property,
|
||||
CS_Status* status) const;
|
||||
|
||||
protected:
|
||||
// Get a property; must be called with m_mutex held.
|
||||
PropertyImpl* GetProperty(int property) {
|
||||
if (property <= 0 || static_cast<size_t>(property) > m_propertyData.size())
|
||||
return nullptr;
|
||||
return m_propertyData[property - 1].get();
|
||||
}
|
||||
const PropertyImpl* GetProperty(int property) const {
|
||||
if (property <= 0 || static_cast<size_t>(property) > m_propertyData.size())
|
||||
return nullptr;
|
||||
return m_propertyData[property - 1].get();
|
||||
}
|
||||
// Create or update a property; must be called with m_mutex held.
|
||||
// @tparam NewFunc functor that returns a std::unique_ptr<PropertyImpl>
|
||||
// @tparam UpdateFunc functor that takes a PropertyImpl&.
|
||||
template <typename NewFunc, typename UpdateFunc>
|
||||
int CreateOrUpdateProperty(wpi::StringRef name, NewFunc newFunc,
|
||||
UpdateFunc updateFunc) {
|
||||
int& ndx = m_properties[name];
|
||||
if (ndx == 0) {
|
||||
// create a new index
|
||||
ndx = m_propertyData.size() + 1;
|
||||
m_propertyData.emplace_back(newFunc());
|
||||
} else {
|
||||
// update existing
|
||||
updateFunc(*GetProperty(ndx));
|
||||
}
|
||||
return ndx;
|
||||
}
|
||||
|
||||
// Create an "empty" property. This is called by GetPropertyIndex to create
|
||||
// properties that don't exist (as GetPropertyIndex can't fail).
|
||||
// Note: called with m_mutex held.
|
||||
// The default implementation simply creates a PropertyImpl object.
|
||||
virtual std::unique_ptr<PropertyImpl> CreateEmptyProperty(
|
||||
wpi::StringRef name) const;
|
||||
|
||||
// Cache properties. Implementations must return false and set status to
|
||||
// CS_SOURCE_IS_DISCONNECTED if not possible to cache.
|
||||
// The default implementation simply sets m_property_cached to true.
|
||||
virtual bool CacheProperties(CS_Status* status) const;
|
||||
|
||||
virtual void NotifyPropertyCreated(int propIndex, PropertyImpl& prop) = 0;
|
||||
|
||||
// Update property value; must be called with m_mutex held.
|
||||
virtual void UpdatePropertyValue(int property, bool setString, int value,
|
||||
wpi::StringRef valueStr) = 0;
|
||||
|
||||
// Whether CacheProperties() has been successful at least once (and thus
|
||||
// should not be called again)
|
||||
mutable std::atomic_bool m_properties_cached{false};
|
||||
|
||||
mutable wpi::mutex m_mutex;
|
||||
|
||||
// Cached properties (protected with m_mutex)
|
||||
mutable std::vector<std::unique_ptr<PropertyImpl>> m_propertyData;
|
||||
mutable wpi::StringMap<int> m_properties;
|
||||
};
|
||||
|
||||
} // namespace cs
|
||||
|
||||
#endif // CSCORE_PROPERTYCONTAINER_H_
|
||||
64
cscore/src/main/native/cpp/PropertyImpl.cpp
Normal file
64
cscore/src/main/native/cpp/PropertyImpl.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2016-2018 FIRST. 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "PropertyImpl.h"
|
||||
|
||||
using namespace cs;
|
||||
|
||||
PropertyImpl::PropertyImpl(wpi::StringRef name_) : name{name_} {}
|
||||
PropertyImpl::PropertyImpl(wpi::StringRef name_, CS_PropertyKind kind_,
|
||||
int step_, int defaultValue_, int value_)
|
||||
: name{name_},
|
||||
propKind{kind_},
|
||||
step{step_},
|
||||
defaultValue{defaultValue_},
|
||||
value{value_} {}
|
||||
PropertyImpl::PropertyImpl(wpi::StringRef name_, CS_PropertyKind kind_,
|
||||
int minimum_, int maximum_, int step_,
|
||||
int defaultValue_, int value_)
|
||||
: name{name_},
|
||||
propKind{kind_},
|
||||
hasMinimum{true},
|
||||
hasMaximum{true},
|
||||
minimum{minimum_},
|
||||
maximum{maximum_},
|
||||
step{step_},
|
||||
defaultValue{defaultValue_},
|
||||
value{value_} {}
|
||||
|
||||
void PropertyImpl::SetValue(int v) {
|
||||
int oldValue = value;
|
||||
if (hasMinimum && v < minimum)
|
||||
value = minimum;
|
||||
else if (hasMaximum && v > maximum)
|
||||
value = maximum;
|
||||
else
|
||||
value = v;
|
||||
bool wasValueSet = valueSet;
|
||||
valueSet = true;
|
||||
if (!wasValueSet || value != oldValue) changed();
|
||||
}
|
||||
|
||||
void PropertyImpl::SetValue(wpi::StringRef v) {
|
||||
bool valueChanged = false;
|
||||
if (valueStr != v) {
|
||||
valueStr = v;
|
||||
valueChanged = true;
|
||||
}
|
||||
bool wasValueSet = valueSet;
|
||||
valueSet = true;
|
||||
if (!wasValueSet || valueChanged) changed();
|
||||
}
|
||||
|
||||
void PropertyImpl::SetDefaultValue(int v) {
|
||||
if (hasMinimum && v < minimum)
|
||||
defaultValue = minimum;
|
||||
else if (hasMaximum && v > maximum)
|
||||
defaultValue = maximum;
|
||||
else
|
||||
defaultValue = v;
|
||||
}
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <wpi/Signal.h>
|
||||
#include <wpi/StringRef.h>
|
||||
|
||||
#include "cscore_c.h"
|
||||
@@ -21,41 +22,18 @@ namespace cs {
|
||||
class PropertyImpl {
|
||||
public:
|
||||
PropertyImpl() = default;
|
||||
explicit PropertyImpl(wpi::StringRef name_) : name{name_} {}
|
||||
explicit PropertyImpl(wpi::StringRef name_);
|
||||
PropertyImpl(wpi::StringRef name_, CS_PropertyKind kind_, int step_,
|
||||
int defaultValue_, int value_)
|
||||
: name{name_},
|
||||
propKind{kind_},
|
||||
step{step_},
|
||||
defaultValue{defaultValue_},
|
||||
value{value_} {}
|
||||
int defaultValue_, int value_);
|
||||
PropertyImpl(wpi::StringRef name_, CS_PropertyKind kind_, int minimum_,
|
||||
int maximum_, int step_, int defaultValue_, int value_);
|
||||
virtual ~PropertyImpl() = default;
|
||||
PropertyImpl(const PropertyImpl& oth) = delete;
|
||||
PropertyImpl& operator=(const PropertyImpl& oth) = delete;
|
||||
|
||||
void SetValue(int v) {
|
||||
if (hasMinimum && v < minimum)
|
||||
value = minimum;
|
||||
else if (hasMaximum && v > maximum)
|
||||
value = maximum;
|
||||
else
|
||||
value = v;
|
||||
valueSet = true;
|
||||
}
|
||||
|
||||
void SetValue(wpi::StringRef v) {
|
||||
valueStr = v;
|
||||
valueSet = true;
|
||||
}
|
||||
|
||||
void SetDefaultValue(int v) {
|
||||
if (hasMinimum && v < minimum)
|
||||
defaultValue = minimum;
|
||||
else if (hasMaximum && v > maximum)
|
||||
defaultValue = maximum;
|
||||
else
|
||||
defaultValue = v;
|
||||
}
|
||||
void SetValue(int v);
|
||||
void SetValue(wpi::StringRef v);
|
||||
void SetDefaultValue(int v);
|
||||
|
||||
std::string name;
|
||||
CS_PropertyKind propKind{CS_PROP_NONE};
|
||||
@@ -69,6 +47,9 @@ class PropertyImpl {
|
||||
std::string valueStr;
|
||||
std::vector<std::string> enumChoices;
|
||||
bool valueSet{false};
|
||||
|
||||
// emitted when value changes
|
||||
wpi::sig::Signal<> changed;
|
||||
};
|
||||
|
||||
} // namespace cs
|
||||
|
||||
@@ -96,4 +96,33 @@ wpi::StringRef SinkImpl::GetError(wpi::SmallVectorImpl<char>& buf) const {
|
||||
return wpi::StringRef{buf.data(), buf.size()};
|
||||
}
|
||||
|
||||
void SinkImpl::NotifyPropertyCreated(int propIndex, PropertyImpl& prop) {
|
||||
auto& notifier = Notifier::GetInstance();
|
||||
notifier.NotifySinkProperty(*this, CS_SINK_PROPERTY_CREATED, prop.name,
|
||||
propIndex, prop.propKind, prop.value,
|
||||
prop.valueStr);
|
||||
// also notify choices updated event for enum types
|
||||
if (prop.propKind == CS_PROP_ENUM)
|
||||
notifier.NotifySinkProperty(*this, CS_SINK_PROPERTY_CHOICES_UPDATED,
|
||||
prop.name, propIndex, prop.propKind, prop.value,
|
||||
wpi::StringRef{});
|
||||
}
|
||||
|
||||
void SinkImpl::UpdatePropertyValue(int property, bool setString, int value,
|
||||
wpi::StringRef valueStr) {
|
||||
auto prop = GetProperty(property);
|
||||
if (!prop) return;
|
||||
|
||||
if (setString)
|
||||
prop->SetValue(valueStr);
|
||||
else
|
||||
prop->SetValue(value);
|
||||
|
||||
// Only notify updates after we've notified created
|
||||
if (m_properties_cached)
|
||||
Notifier::GetInstance().NotifySinkProperty(
|
||||
*this, CS_SINK_PROPERTY_VALUE_UPDATED, prop->name, property,
|
||||
prop->propKind, prop->value, prop->valueStr);
|
||||
}
|
||||
|
||||
void SinkImpl::SetSourceImpl(std::shared_ptr<SourceImpl> source) {}
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace cs {
|
||||
|
||||
class Frame;
|
||||
|
||||
class SinkImpl {
|
||||
class SinkImpl : public PropertyContainer {
|
||||
public:
|
||||
explicit SinkImpl(wpi::StringRef name);
|
||||
virtual ~SinkImpl();
|
||||
@@ -47,9 +47,12 @@ class SinkImpl {
|
||||
wpi::StringRef GetError(wpi::SmallVectorImpl<char>& buf) const;
|
||||
|
||||
protected:
|
||||
virtual void SetSourceImpl(std::shared_ptr<SourceImpl> source);
|
||||
// PropertyContainer implementation
|
||||
void NotifyPropertyCreated(int propIndex, PropertyImpl& prop) override;
|
||||
void UpdatePropertyValue(int property, bool setString, int value,
|
||||
wpi::StringRef valueStr) override;
|
||||
|
||||
mutable wpi::mutex m_mutex;
|
||||
virtual void SetSourceImpl(std::shared_ptr<SourceImpl> source);
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
|
||||
@@ -94,150 +94,6 @@ void SourceImpl::Wakeup() {
|
||||
m_frameCv.notify_all();
|
||||
}
|
||||
|
||||
int SourceImpl::GetPropertyIndex(wpi::StringRef name) const {
|
||||
// We can't fail, so instead we create a new index if caching fails.
|
||||
CS_Status status = 0;
|
||||
if (!m_properties_cached) CacheProperties(&status);
|
||||
std::lock_guard<wpi::mutex> lock(m_mutex);
|
||||
int& ndx = m_properties[name];
|
||||
if (ndx == 0) {
|
||||
// create a new index
|
||||
ndx = m_propertyData.size() + 1;
|
||||
m_propertyData.emplace_back(CreateEmptyProperty(name));
|
||||
}
|
||||
return ndx;
|
||||
}
|
||||
|
||||
wpi::ArrayRef<int> SourceImpl::EnumerateProperties(
|
||||
wpi::SmallVectorImpl<int>& vec, CS_Status* status) const {
|
||||
if (!m_properties_cached && !CacheProperties(status))
|
||||
return wpi::ArrayRef<int>{};
|
||||
std::lock_guard<wpi::mutex> lock(m_mutex);
|
||||
for (int i = 0; i < static_cast<int>(m_propertyData.size()); ++i) {
|
||||
if (m_propertyData[i]) vec.push_back(i + 1);
|
||||
}
|
||||
return vec;
|
||||
}
|
||||
|
||||
CS_PropertyKind SourceImpl::GetPropertyKind(int property) const {
|
||||
CS_Status status = 0;
|
||||
if (!m_properties_cached && !CacheProperties(&status)) return CS_PROP_NONE;
|
||||
std::lock_guard<wpi::mutex> lock(m_mutex);
|
||||
auto prop = GetProperty(property);
|
||||
if (!prop) return CS_PROP_NONE;
|
||||
return prop->propKind;
|
||||
}
|
||||
|
||||
wpi::StringRef SourceImpl::GetPropertyName(int property,
|
||||
wpi::SmallVectorImpl<char>& buf,
|
||||
CS_Status* status) const {
|
||||
if (!m_properties_cached && !CacheProperties(status)) return wpi::StringRef{};
|
||||
std::lock_guard<wpi::mutex> lock(m_mutex);
|
||||
auto prop = GetProperty(property);
|
||||
if (!prop) {
|
||||
*status = CS_INVALID_PROPERTY;
|
||||
return wpi::StringRef{};
|
||||
}
|
||||
// safe to not copy because we never modify it after caching
|
||||
return prop->name;
|
||||
}
|
||||
|
||||
int SourceImpl::GetProperty(int property, CS_Status* status) const {
|
||||
if (!m_properties_cached && !CacheProperties(status)) return 0;
|
||||
std::lock_guard<wpi::mutex> lock(m_mutex);
|
||||
auto prop = GetProperty(property);
|
||||
if (!prop) {
|
||||
*status = CS_INVALID_PROPERTY;
|
||||
return 0;
|
||||
}
|
||||
if ((prop->propKind & (CS_PROP_BOOLEAN | CS_PROP_INTEGER | CS_PROP_ENUM)) ==
|
||||
0) {
|
||||
*status = CS_WRONG_PROPERTY_TYPE;
|
||||
return 0;
|
||||
}
|
||||
return prop->value;
|
||||
}
|
||||
|
||||
int SourceImpl::GetPropertyMin(int property, CS_Status* status) const {
|
||||
if (!m_properties_cached && !CacheProperties(status)) return 0;
|
||||
std::lock_guard<wpi::mutex> lock(m_mutex);
|
||||
auto prop = GetProperty(property);
|
||||
if (!prop) {
|
||||
*status = CS_INVALID_PROPERTY;
|
||||
return 0;
|
||||
}
|
||||
return prop->minimum;
|
||||
}
|
||||
|
||||
int SourceImpl::GetPropertyMax(int property, CS_Status* status) const {
|
||||
if (!m_properties_cached && !CacheProperties(status)) return 0;
|
||||
std::lock_guard<wpi::mutex> lock(m_mutex);
|
||||
auto prop = GetProperty(property);
|
||||
if (!prop) {
|
||||
*status = CS_INVALID_PROPERTY;
|
||||
return 0;
|
||||
}
|
||||
return prop->maximum;
|
||||
}
|
||||
|
||||
int SourceImpl::GetPropertyStep(int property, CS_Status* status) const {
|
||||
if (!m_properties_cached && !CacheProperties(status)) return 0;
|
||||
std::lock_guard<wpi::mutex> lock(m_mutex);
|
||||
auto prop = GetProperty(property);
|
||||
if (!prop) {
|
||||
*status = CS_INVALID_PROPERTY;
|
||||
return 0;
|
||||
}
|
||||
return prop->step;
|
||||
}
|
||||
|
||||
int SourceImpl::GetPropertyDefault(int property, CS_Status* status) const {
|
||||
if (!m_properties_cached && !CacheProperties(status)) return 0;
|
||||
std::lock_guard<wpi::mutex> lock(m_mutex);
|
||||
auto prop = GetProperty(property);
|
||||
if (!prop) {
|
||||
*status = CS_INVALID_PROPERTY;
|
||||
return 0;
|
||||
}
|
||||
return prop->defaultValue;
|
||||
}
|
||||
|
||||
wpi::StringRef SourceImpl::GetStringProperty(int property,
|
||||
wpi::SmallVectorImpl<char>& buf,
|
||||
CS_Status* status) const {
|
||||
if (!m_properties_cached && !CacheProperties(status)) return wpi::StringRef{};
|
||||
std::lock_guard<wpi::mutex> lock(m_mutex);
|
||||
auto prop = GetProperty(property);
|
||||
if (!prop) {
|
||||
*status = CS_INVALID_PROPERTY;
|
||||
return wpi::StringRef{};
|
||||
}
|
||||
if (prop->propKind != CS_PROP_STRING) {
|
||||
*status = CS_WRONG_PROPERTY_TYPE;
|
||||
return wpi::StringRef{};
|
||||
}
|
||||
buf.clear();
|
||||
buf.append(prop->valueStr.begin(), prop->valueStr.end());
|
||||
return wpi::StringRef(buf.data(), buf.size());
|
||||
}
|
||||
|
||||
std::vector<std::string> SourceImpl::GetEnumPropertyChoices(
|
||||
int property, CS_Status* status) const {
|
||||
if (!m_properties_cached && !CacheProperties(status))
|
||||
return std::vector<std::string>{};
|
||||
std::lock_guard<wpi::mutex> lock(m_mutex);
|
||||
auto prop = GetProperty(property);
|
||||
if (!prop) {
|
||||
*status = CS_INVALID_PROPERTY;
|
||||
return std::vector<std::string>{};
|
||||
}
|
||||
if (prop->propKind != CS_PROP_ENUM) {
|
||||
*status = CS_WRONG_PROPERTY_TYPE;
|
||||
return std::vector<std::string>{};
|
||||
}
|
||||
return prop->enumChoices;
|
||||
}
|
||||
|
||||
VideoMode SourceImpl::GetVideoMode(CS_Status* status) const {
|
||||
if (!m_properties_cached && !CacheProperties(status)) return VideoMode{};
|
||||
std::lock_guard<wpi::mutex> lock(m_mutex);
|
||||
|
||||
@@ -15,19 +15,18 @@
|
||||
#include <vector>
|
||||
|
||||
#include <wpi/ArrayRef.h>
|
||||
#include <wpi/StringMap.h>
|
||||
#include <wpi/StringRef.h>
|
||||
#include <wpi/condition_variable.h>
|
||||
#include <wpi/mutex.h>
|
||||
|
||||
#include "Frame.h"
|
||||
#include "Image.h"
|
||||
#include "PropertyImpl.h"
|
||||
#include "PropertyContainer.h"
|
||||
#include "cscore_cpp.h"
|
||||
|
||||
namespace cs {
|
||||
|
||||
class SourceImpl {
|
||||
class SourceImpl : public PropertyContainer {
|
||||
friend class Frame;
|
||||
|
||||
public:
|
||||
@@ -89,27 +88,6 @@ class SourceImpl {
|
||||
// Force a wakeup of all GetNextFrame() callers by sending an empty frame.
|
||||
void Wakeup();
|
||||
|
||||
// Property functions
|
||||
int GetPropertyIndex(wpi::StringRef name) const;
|
||||
wpi::ArrayRef<int> EnumerateProperties(wpi::SmallVectorImpl<int>& vec,
|
||||
CS_Status* status) const;
|
||||
CS_PropertyKind GetPropertyKind(int property) const;
|
||||
wpi::StringRef GetPropertyName(int property, wpi::SmallVectorImpl<char>& buf,
|
||||
CS_Status* status) const;
|
||||
int GetProperty(int property, CS_Status* status) const;
|
||||
virtual void SetProperty(int property, int value, CS_Status* status) = 0;
|
||||
int GetPropertyMin(int property, CS_Status* status) const;
|
||||
int GetPropertyMax(int property, CS_Status* status) const;
|
||||
int GetPropertyStep(int property, CS_Status* status) const;
|
||||
int GetPropertyDefault(int property, CS_Status* status) const;
|
||||
wpi::StringRef GetStringProperty(int property,
|
||||
wpi::SmallVectorImpl<char>& buf,
|
||||
CS_Status* status) const;
|
||||
virtual void SetStringProperty(int property, wpi::StringRef value,
|
||||
CS_Status* status) = 0;
|
||||
std::vector<std::string> GetEnumPropertyChoices(int property,
|
||||
CS_Status* status) const;
|
||||
|
||||
// Standard common camera properties
|
||||
virtual void SetBrightness(int brightness, CS_Status* status) = 0;
|
||||
virtual int GetBrightness(CS_Status* status) const = 0;
|
||||
@@ -137,6 +115,10 @@ class SourceImpl {
|
||||
int width, int height, size_t size);
|
||||
|
||||
protected:
|
||||
void NotifyPropertyCreated(int propIndex, PropertyImpl& prop) override;
|
||||
void UpdatePropertyValue(int property, bool setString, int value,
|
||||
wpi::StringRef valueStr) override;
|
||||
|
||||
void PutFrame(VideoMode::PixelFormat pixelFormat, int width, int height,
|
||||
wpi::StringRef data, Frame::Time time);
|
||||
void PutFrame(std::unique_ptr<Image> image, Frame::Time time);
|
||||
@@ -150,45 +132,10 @@ class SourceImpl {
|
||||
std::atomic_int m_numSinksEnabled{0};
|
||||
|
||||
protected:
|
||||
// Get a property; must be called with m_mutex held.
|
||||
PropertyImpl* GetProperty(int property) {
|
||||
if (property <= 0 || static_cast<size_t>(property) > m_propertyData.size())
|
||||
return nullptr;
|
||||
return m_propertyData[property - 1].get();
|
||||
}
|
||||
const PropertyImpl* GetProperty(int property) const {
|
||||
if (property <= 0 || static_cast<size_t>(property) > m_propertyData.size())
|
||||
return nullptr;
|
||||
return m_propertyData[property - 1].get();
|
||||
}
|
||||
|
||||
// Create an "empty" property. This is called by GetPropertyIndex to create
|
||||
// properties that don't exist (as GetPropertyIndex can't fail).
|
||||
// Note: called with m_mutex held.
|
||||
virtual std::unique_ptr<PropertyImpl> CreateEmptyProperty(
|
||||
wpi::StringRef name) const = 0;
|
||||
|
||||
// Cache properties. Implementations must return false and set status to
|
||||
// CS_SOURCE_IS_DISCONNECTED if not possible to cache.
|
||||
virtual bool CacheProperties(CS_Status* status) const = 0;
|
||||
|
||||
void NotifyPropertyCreated(int propIndex, PropertyImpl& prop);
|
||||
|
||||
// Update property value; must be called with m_mutex held.
|
||||
void UpdatePropertyValue(int property, bool setString, int value,
|
||||
wpi::StringRef valueStr);
|
||||
|
||||
// Cached properties and video modes (protected with m_mutex)
|
||||
mutable std::vector<std::unique_ptr<PropertyImpl>> m_propertyData;
|
||||
mutable wpi::StringMap<int> m_properties;
|
||||
// Cached video modes (protected with m_mutex)
|
||||
mutable std::vector<VideoMode> m_videoModes;
|
||||
// Current video mode
|
||||
mutable VideoMode m_mode;
|
||||
// Whether CacheProperties() has been successful at least once (and thus
|
||||
// should not be called again)
|
||||
mutable std::atomic_bool m_properties_cached{false};
|
||||
|
||||
mutable wpi::mutex m_mutex;
|
||||
|
||||
private:
|
||||
void ReleaseImage(std::unique_ptr<Image> image);
|
||||
|
||||
@@ -242,6 +242,22 @@ char* CS_GetSinkDescription(CS_Sink sink, CS_Status* status) {
|
||||
return cs::ConvertToC(str);
|
||||
}
|
||||
|
||||
CS_Property CS_GetSinkProperty(CS_Sink sink, const char* name,
|
||||
CS_Status* status) {
|
||||
return cs::GetSinkProperty(sink, name, status);
|
||||
}
|
||||
|
||||
CS_Property* CS_EnumerateSinkProperties(CS_Sink sink, int* count,
|
||||
CS_Status* status) {
|
||||
wpi::SmallVector<CS_Property, 32> buf;
|
||||
auto vec = cs::EnumerateSinkProperties(sink, buf, status);
|
||||
CS_Property* out = static_cast<CS_Property*>(
|
||||
wpi::CheckedMalloc(vec.size() * sizeof(CS_Property)));
|
||||
*count = vec.size();
|
||||
std::copy(vec.begin(), vec.end(), out);
|
||||
return out;
|
||||
}
|
||||
|
||||
void CS_SetSinkSource(CS_Sink sink, CS_Source source, CS_Status* status) {
|
||||
return cs::SetSinkSource(sink, source, status);
|
||||
}
|
||||
|
||||
@@ -20,28 +20,39 @@
|
||||
#include "Log.h"
|
||||
#include "NetworkListener.h"
|
||||
#include "Notifier.h"
|
||||
#include "PropertyContainer.h"
|
||||
#include "SinkImpl.h"
|
||||
#include "SourceImpl.h"
|
||||
#include "Telemetry.h"
|
||||
|
||||
using namespace cs;
|
||||
|
||||
static std::shared_ptr<SourceImpl> GetPropertySource(CS_Property propertyHandle,
|
||||
int* propertyIndex,
|
||||
CS_Status* status) {
|
||||
static std::shared_ptr<PropertyContainer> GetPropertyContainer(
|
||||
CS_Property propertyHandle, int* propertyIndex, CS_Status* status) {
|
||||
std::shared_ptr<PropertyContainer> container;
|
||||
Handle handle{propertyHandle};
|
||||
int i = handle.GetParentIndex();
|
||||
if (i < 0) {
|
||||
*status = CS_INVALID_HANDLE;
|
||||
return nullptr;
|
||||
}
|
||||
auto data = Sources::GetInstance().Get(Handle{i, Handle::kSource});
|
||||
if (!data) {
|
||||
if (handle.IsType(Handle::kProperty)) {
|
||||
int i = handle.GetParentIndex();
|
||||
auto data = Sources::GetInstance().Get(Handle{i, Handle::kSource});
|
||||
if (!data) {
|
||||
*status = CS_INVALID_HANDLE;
|
||||
return nullptr;
|
||||
}
|
||||
container = data->source;
|
||||
} else if (handle.IsType(Handle::kSinkProperty)) {
|
||||
int i = handle.GetParentIndex();
|
||||
auto data = Sinks::GetInstance().Get(Handle{i, Handle::kSink});
|
||||
if (!data) {
|
||||
*status = CS_INVALID_HANDLE;
|
||||
return nullptr;
|
||||
}
|
||||
container = data->sink;
|
||||
} else {
|
||||
*status = CS_INVALID_HANDLE;
|
||||
return nullptr;
|
||||
}
|
||||
*propertyIndex = handle.GetIndex();
|
||||
return data->source;
|
||||
return container;
|
||||
}
|
||||
|
||||
namespace cs {
|
||||
@@ -52,101 +63,101 @@ namespace cs {
|
||||
|
||||
CS_PropertyKind GetPropertyKind(CS_Property property, CS_Status* status) {
|
||||
int propertyIndex;
|
||||
auto source = GetPropertySource(property, &propertyIndex, status);
|
||||
if (!source) return CS_PROP_NONE;
|
||||
return source->GetPropertyKind(propertyIndex);
|
||||
auto container = GetPropertyContainer(property, &propertyIndex, status);
|
||||
if (!container) return CS_PROP_NONE;
|
||||
return container->GetPropertyKind(propertyIndex);
|
||||
}
|
||||
|
||||
std::string GetPropertyName(CS_Property property, CS_Status* status) {
|
||||
wpi::SmallString<128> buf;
|
||||
int propertyIndex;
|
||||
auto source = GetPropertySource(property, &propertyIndex, status);
|
||||
if (!source) return std::string{};
|
||||
return source->GetPropertyName(propertyIndex, buf, status);
|
||||
auto container = GetPropertyContainer(property, &propertyIndex, status);
|
||||
if (!container) return std::string{};
|
||||
return container->GetPropertyName(propertyIndex, buf, status);
|
||||
}
|
||||
|
||||
wpi::StringRef GetPropertyName(CS_Property property,
|
||||
wpi::SmallVectorImpl<char>& buf,
|
||||
CS_Status* status) {
|
||||
int propertyIndex;
|
||||
auto source = GetPropertySource(property, &propertyIndex, status);
|
||||
if (!source) return wpi::StringRef{};
|
||||
return source->GetPropertyName(propertyIndex, buf, status);
|
||||
auto container = GetPropertyContainer(property, &propertyIndex, status);
|
||||
if (!container) return wpi::StringRef{};
|
||||
return container->GetPropertyName(propertyIndex, buf, status);
|
||||
}
|
||||
|
||||
int GetProperty(CS_Property property, CS_Status* status) {
|
||||
int propertyIndex;
|
||||
auto source = GetPropertySource(property, &propertyIndex, status);
|
||||
if (!source) return false;
|
||||
return source->GetProperty(propertyIndex, status);
|
||||
auto container = GetPropertyContainer(property, &propertyIndex, status);
|
||||
if (!container) return false;
|
||||
return container->GetProperty(propertyIndex, status);
|
||||
}
|
||||
|
||||
void SetProperty(CS_Property property, int value, CS_Status* status) {
|
||||
int propertyIndex;
|
||||
auto source = GetPropertySource(property, &propertyIndex, status);
|
||||
if (!source) return;
|
||||
source->SetProperty(propertyIndex, value, status);
|
||||
auto container = GetPropertyContainer(property, &propertyIndex, status);
|
||||
if (!container) return;
|
||||
container->SetProperty(propertyIndex, value, status);
|
||||
}
|
||||
|
||||
int GetPropertyMin(CS_Property property, CS_Status* status) {
|
||||
int propertyIndex;
|
||||
auto source = GetPropertySource(property, &propertyIndex, status);
|
||||
if (!source) return 0.0;
|
||||
return source->GetPropertyMin(propertyIndex, status);
|
||||
auto container = GetPropertyContainer(property, &propertyIndex, status);
|
||||
if (!container) return 0.0;
|
||||
return container->GetPropertyMin(propertyIndex, status);
|
||||
}
|
||||
|
||||
int GetPropertyMax(CS_Property property, CS_Status* status) {
|
||||
int propertyIndex;
|
||||
auto source = GetPropertySource(property, &propertyIndex, status);
|
||||
if (!source) return 0.0;
|
||||
return source->GetPropertyMax(propertyIndex, status);
|
||||
auto container = GetPropertyContainer(property, &propertyIndex, status);
|
||||
if (!container) return 0.0;
|
||||
return container->GetPropertyMax(propertyIndex, status);
|
||||
}
|
||||
|
||||
int GetPropertyStep(CS_Property property, CS_Status* status) {
|
||||
int propertyIndex;
|
||||
auto source = GetPropertySource(property, &propertyIndex, status);
|
||||
if (!source) return 0.0;
|
||||
return source->GetPropertyStep(propertyIndex, status);
|
||||
auto container = GetPropertyContainer(property, &propertyIndex, status);
|
||||
if (!container) return 0.0;
|
||||
return container->GetPropertyStep(propertyIndex, status);
|
||||
}
|
||||
|
||||
int GetPropertyDefault(CS_Property property, CS_Status* status) {
|
||||
int propertyIndex;
|
||||
auto source = GetPropertySource(property, &propertyIndex, status);
|
||||
if (!source) return 0.0;
|
||||
return source->GetPropertyDefault(propertyIndex, status);
|
||||
auto container = GetPropertyContainer(property, &propertyIndex, status);
|
||||
if (!container) return 0.0;
|
||||
return container->GetPropertyDefault(propertyIndex, status);
|
||||
}
|
||||
|
||||
std::string GetStringProperty(CS_Property property, CS_Status* status) {
|
||||
wpi::SmallString<128> buf;
|
||||
int propertyIndex;
|
||||
auto source = GetPropertySource(property, &propertyIndex, status);
|
||||
if (!source) return std::string{};
|
||||
return source->GetStringProperty(propertyIndex, buf, status);
|
||||
auto container = GetPropertyContainer(property, &propertyIndex, status);
|
||||
if (!container) return std::string{};
|
||||
return container->GetStringProperty(propertyIndex, buf, status);
|
||||
}
|
||||
|
||||
wpi::StringRef GetStringProperty(CS_Property property,
|
||||
wpi::SmallVectorImpl<char>& buf,
|
||||
CS_Status* status) {
|
||||
int propertyIndex;
|
||||
auto source = GetPropertySource(property, &propertyIndex, status);
|
||||
if (!source) return wpi::StringRef{};
|
||||
return source->GetStringProperty(propertyIndex, buf, status);
|
||||
auto container = GetPropertyContainer(property, &propertyIndex, status);
|
||||
if (!container) return wpi::StringRef{};
|
||||
return container->GetStringProperty(propertyIndex, buf, status);
|
||||
}
|
||||
|
||||
void SetStringProperty(CS_Property property, wpi::StringRef value,
|
||||
CS_Status* status) {
|
||||
int propertyIndex;
|
||||
auto source = GetPropertySource(property, &propertyIndex, status);
|
||||
if (!source) return;
|
||||
source->SetStringProperty(propertyIndex, value, status);
|
||||
auto container = GetPropertyContainer(property, &propertyIndex, status);
|
||||
if (!container) return;
|
||||
container->SetStringProperty(propertyIndex, value, status);
|
||||
}
|
||||
|
||||
std::vector<std::string> GetEnumPropertyChoices(CS_Property property,
|
||||
CS_Status* status) {
|
||||
int propertyIndex;
|
||||
auto source = GetPropertySource(property, &propertyIndex, status);
|
||||
if (!source) return std::vector<std::string>{};
|
||||
return source->GetEnumPropertyChoices(propertyIndex, status);
|
||||
auto container = GetPropertyContainer(property, &propertyIndex, status);
|
||||
if (!container) return std::vector<std::string>{};
|
||||
return container->GetEnumPropertyChoices(propertyIndex, status);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -478,6 +489,34 @@ wpi::StringRef GetSinkDescription(CS_Sink sink, wpi::SmallVectorImpl<char>& buf,
|
||||
return data->sink->GetDescription(buf);
|
||||
}
|
||||
|
||||
CS_Property GetSinkProperty(CS_Sink sink, wpi::StringRef name,
|
||||
CS_Status* status) {
|
||||
auto data = Sinks::GetInstance().Get(sink);
|
||||
if (!data) {
|
||||
*status = CS_INVALID_HANDLE;
|
||||
return 0;
|
||||
}
|
||||
int property = data->sink->GetPropertyIndex(name);
|
||||
if (property < 0) {
|
||||
*status = CS_INVALID_HANDLE;
|
||||
return 0;
|
||||
}
|
||||
return Handle{sink, property, Handle::kSinkProperty};
|
||||
}
|
||||
|
||||
wpi::ArrayRef<CS_Property> EnumerateSinkProperties(
|
||||
CS_Sink sink, wpi::SmallVectorImpl<CS_Property>& vec, CS_Status* status) {
|
||||
auto data = Sinks::GetInstance().Get(sink);
|
||||
if (!data) {
|
||||
*status = CS_INVALID_HANDLE;
|
||||
return 0;
|
||||
}
|
||||
wpi::SmallVector<int, 32> properties_buf;
|
||||
for (auto property : data->sink->EnumerateProperties(properties_buf, status))
|
||||
vec.push_back(Handle{sink, property, Handle::kSinkProperty});
|
||||
return vec;
|
||||
}
|
||||
|
||||
void SetSinkSource(CS_Sink sink, CS_Source source, CS_Status* status) {
|
||||
auto data = Sinks::GetInstance().Get(sink);
|
||||
if (!data) {
|
||||
|
||||
@@ -43,6 +43,18 @@ std::vector<VideoSource> VideoSource::EnumerateSources() {
|
||||
return sources;
|
||||
}
|
||||
|
||||
std::vector<VideoProperty> VideoSink::EnumerateProperties() const {
|
||||
wpi::SmallVector<CS_Property, 32> handles_buf;
|
||||
CS_Status status = 0;
|
||||
auto handles = EnumerateSinkProperties(m_handle, handles_buf, &status);
|
||||
|
||||
std::vector<VideoProperty> properties;
|
||||
properties.reserve(handles.size());
|
||||
for (CS_Property handle : handles)
|
||||
properties.emplace_back(VideoProperty{handle});
|
||||
return properties;
|
||||
}
|
||||
|
||||
std::vector<VideoSink> VideoSink::EnumerateSinks() {
|
||||
wpi::SmallVector<CS_Sink, 16> handles_buf;
|
||||
CS_Status status = 0;
|
||||
|
||||
@@ -1192,6 +1192,41 @@ Java_edu_wpi_cscore_CameraServerJNI_getSinkDescription
|
||||
return MakeJString(env, str);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_cscore_CameraServerJNI
|
||||
* Method: getSinkProperty
|
||||
* Signature: (ILjava/lang/String;)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_edu_wpi_cscore_CameraServerJNI_getSinkProperty
|
||||
(JNIEnv* env, jclass, jint sink, jstring name)
|
||||
{
|
||||
if (!name) {
|
||||
nullPointerEx.Throw(env, "name cannot be null");
|
||||
return 0;
|
||||
}
|
||||
CS_Status status = 0;
|
||||
auto val = cs::GetSinkProperty(sink, JStringRef{env, name}, &status);
|
||||
CheckStatus(env, status);
|
||||
return val;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_cscore_CameraServerJNI
|
||||
* Method: enumerateSinkProperties
|
||||
* Signature: (I)[I
|
||||
*/
|
||||
JNIEXPORT jintArray JNICALL
|
||||
Java_edu_wpi_cscore_CameraServerJNI_enumerateSinkProperties
|
||||
(JNIEnv* env, jclass, jint source)
|
||||
{
|
||||
CS_Status status = 0;
|
||||
wpi::SmallVector<CS_Property, 32> buf;
|
||||
auto arr = cs::EnumerateSinkProperties(source, buf, &status);
|
||||
if (!CheckStatus(env, status)) return nullptr;
|
||||
return MakeJIntArray(env, arr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_cscore_CameraServerJNI
|
||||
* Method: setSinkSource
|
||||
|
||||
@@ -154,7 +154,10 @@ enum CS_EventKind {
|
||||
CS_SINK_ENABLED = 0x1000,
|
||||
CS_SINK_DISABLED = 0x2000,
|
||||
CS_NETWORK_INTERFACES_CHANGED = 0x4000,
|
||||
CS_TELEMETRY_UPDATED = 0x8000
|
||||
CS_TELEMETRY_UPDATED = 0x8000,
|
||||
CS_SINK_PROPERTY_CREATED = 0x10000,
|
||||
CS_SINK_PROPERTY_VALUE_UPDATED = 0x20000,
|
||||
CS_SINK_PROPERTY_CHOICES_UPDATED = 0x40000
|
||||
};
|
||||
|
||||
//
|
||||
@@ -315,6 +318,10 @@ CS_Sink CS_CreateCvSinkCallback(const char* name, void* data,
|
||||
enum CS_SinkKind CS_GetSinkKind(CS_Sink sink, CS_Status* status);
|
||||
char* CS_GetSinkName(CS_Sink sink, CS_Status* status);
|
||||
char* CS_GetSinkDescription(CS_Sink sink, CS_Status* status);
|
||||
CS_Property CS_GetSinkProperty(CS_Sink sink, const char* name,
|
||||
CS_Status* status);
|
||||
CS_Property* CS_EnumerateSinkProperties(CS_Sink sink, int* count,
|
||||
CS_Status* status);
|
||||
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);
|
||||
|
||||
@@ -85,7 +85,10 @@ struct RawEvent {
|
||||
kSinkEnabled = CS_SINK_ENABLED,
|
||||
kSinkDisabled = CS_SINK_DISABLED,
|
||||
kNetworkInterfacesChanged = CS_NETWORK_INTERFACES_CHANGED,
|
||||
kTelemetryUpdated = CS_TELEMETRY_UPDATED
|
||||
kTelemetryUpdated = CS_TELEMETRY_UPDATED,
|
||||
kSinkPropertyCreated = CS_SINK_PROPERTY_CREATED,
|
||||
kSinkPropertyValueUpdated = CS_SINK_PROPERTY_VALUE_UPDATED,
|
||||
kSinkPropertyChoicesUpdated = CS_SINK_PROPERTY_CHOICES_UPDATED
|
||||
};
|
||||
|
||||
RawEvent() = default;
|
||||
@@ -265,6 +268,10 @@ wpi::StringRef GetSinkName(CS_Sink sink, wpi::SmallVectorImpl<char>& buf,
|
||||
std::string GetSinkDescription(CS_Sink sink, CS_Status* status);
|
||||
wpi::StringRef GetSinkDescription(CS_Sink sink, wpi::SmallVectorImpl<char>& buf,
|
||||
CS_Status* status);
|
||||
CS_Property GetSinkProperty(CS_Sink sink, wpi::StringRef name,
|
||||
CS_Status* status);
|
||||
wpi::ArrayRef<CS_Property> EnumerateSinkProperties(
|
||||
CS_Sink sink, wpi::SmallVectorImpl<CS_Property>& vec, CS_Status* status);
|
||||
void SetSinkSource(CS_Sink sink, CS_Source source, CS_Status* status);
|
||||
CS_Property GetSinkSourceProperty(CS_Sink sink, wpi::StringRef name,
|
||||
CS_Status* status);
|
||||
|
||||
@@ -502,6 +502,15 @@ class VideoSink {
|
||||
/// Get the sink description. This is sink-kind specific.
|
||||
std::string GetDescription() const;
|
||||
|
||||
/// Get a property of the sink.
|
||||
/// @param name Property name
|
||||
/// @return Property (kind Property::kNone if no property with
|
||||
/// the given name exists)
|
||||
VideoProperty GetProperty(wpi::StringRef name);
|
||||
|
||||
/// Enumerate all properties of this sink.
|
||||
std::vector<VideoProperty> EnumerateProperties() 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
|
||||
/// provide frames to multiple clients.
|
||||
|
||||
@@ -459,6 +459,11 @@ inline std::string VideoSink::GetDescription() const {
|
||||
return GetSinkDescription(m_handle, &m_status);
|
||||
}
|
||||
|
||||
inline VideoProperty VideoSink::GetProperty(wpi::StringRef name) {
|
||||
m_status = 0;
|
||||
return VideoProperty{GetSinkProperty(m_handle, name, &m_status)};
|
||||
}
|
||||
|
||||
inline void VideoSink::SetSource(VideoSource source) {
|
||||
m_status = 0;
|
||||
if (!source)
|
||||
|
||||
Reference in New Issue
Block a user