cscore: Add properties support to VideoSink (#1228)

Refactor the Property internals into PropertyContainer.
This commit is contained in:
Peter Johnson
2018-07-27 22:12:30 -07:00
committed by GitHub
parent c9a75a119a
commit 9398278250
24 changed files with 695 additions and 389 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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;
}

View 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_

View 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;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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