diff --git a/src/Frame.cpp b/src/Frame.cpp new file mode 100644 index 0000000000..ab0d27b5f5 --- /dev/null +++ b/src/Frame.cpp @@ -0,0 +1,14 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) FIRST 2016. 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 "Frame.h" + +#include "SourceImpl.h" + +using namespace cs; + +void Frame::ReleaseFrame() { m_source->ReleaseFrame(m_data); } diff --git a/src/Frame.h b/src/Frame.h new file mode 100644 index 0000000000..0072fe838b --- /dev/null +++ b/src/Frame.h @@ -0,0 +1,83 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) FIRST 2016. 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 CAMERASERVER_FRAME_H_ +#define CAMERASERVER_FRAME_H_ + +#include +#include + +#include "llvm/SmallVector.h" + +#include "Image.h" + +namespace cs { + +class SourceImpl; + +class Frame { + friend class SourceImpl; + + struct Data { + std::atomic_int refcount{0}; + std::chrono::system_clock::time_point timestamp; + llvm::SmallVector images; + }; + + public: + Frame(SourceImpl& source, Data* data) : m_source(&source), m_data(data) { + if (m_data) ++(m_data->refcount); + } + + Frame(const Frame& frame) : m_source(frame.m_source), m_data(frame.m_data) { + if (m_data) ++(m_data->refcount); + } + + Frame(Frame&& frame) : m_source(frame.m_source), m_data(frame.m_data) { + frame.m_data = nullptr; + } + + ~Frame() { DecRef(); } + + Frame& operator=(const Frame& frame) { + DecRef(); + m_source = frame.m_source; + m_data = frame.m_data; + if (m_data) ++(m_data->refcount); + return *this; + } + + explicit operator bool() const { return m_data; } + + std::size_t size(std::size_t channel) const { + if (!m_data || channel >= m_data->images.size()) return 0; + return m_data->images[channel].size(); + } + + const char* data(std::size_t channel) const { + if (!m_data || channel >= m_data->images.size()) return nullptr; + return m_data->images[channel].data(); + } + + std::chrono::system_clock::time_point time() const { + if (!m_data) return std::chrono::system_clock::time_point{}; + return m_data->timestamp; + } + + private: + void DecRef() { + if (m_data && --(m_data->refcount) == 0) ReleaseFrame(); + } + void ReleaseFrame(); + + SourceImpl* m_source; + Data* m_data; +}; + +} // namespace cs + +#endif // CAMERASERVER_FRAME_H_ diff --git a/src/Handle.cpp b/src/Handle.cpp new file mode 100644 index 0000000000..efa198c77f --- /dev/null +++ b/src/Handle.cpp @@ -0,0 +1,13 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) FIRST 2016. 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 "Handle.h" + +using namespace cs; + +ATOMIC_STATIC_INIT(Sources) +ATOMIC_STATIC_INIT(Sinks) diff --git a/src/Handle.h b/src/Handle.h new file mode 100644 index 0000000000..4496d81763 --- /dev/null +++ b/src/Handle.h @@ -0,0 +1,107 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) FIRST 2016. 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 CAMERASERVER_HANDLE_H_ +#define CAMERASERVER_HANDLE_H_ + +#include + +#include "llvm/StringRef.h" + +#include "cameraserver_c.h" +#include "UnlimitedHandleResource.h" + +namespace cs { + +class SinkImpl; +class SourceImpl; + +// Handle data layout: +// Bits 0-15: Handle index +// Bits 16-23: Subindex (property only) +// Bits 24-30: Type + +class Handle { + public: + enum Type { + kUndefined = 0, + kProperty = 0x40, + kSource, + kSink, + kListener + }; + enum { kIndexMax = 0xffff }; + + Handle(CS_Handle handle) : m_handle(handle) {} + operator CS_Handle() const { return m_handle; } + + Handle(int index, Type type) { + if (index < 0) { + m_handle = 0; + return; + } + m_handle = ((static_cast(type) & 0x7f) << 24) | (index & 0xffff); + } + Handle(int index, int property, Type type) { + if (index < 0 || property < 0) { + m_handle = 0; + return; + } + m_handle = ((static_cast(type) & 0x7f) << 24) | + ((property & 0xff) << 16) | (index & 0xffff); + } + + int GetIndex() const { return static_cast(m_handle) & 0xffff; } + Type GetType() const { + return static_cast((static_cast(m_handle) >> 24) & 0xff); + } + bool IsType(Type type) const { return type == GetType(); } + int GetTypedIndex(Type type) const { return IsType(type) ? GetIndex() : -1; } + int GetSubIndex() const { return (static_cast(m_handle) >> 16) & 0xff; } + + private: + CS_Handle m_handle; +}; + +struct SourceData { + enum Type { + kUnknown = 0, + kUSB, + kHTTP, + kCv + }; + SourceData(Type type_, std::shared_ptr source_) + : type{type_}, refCount{0}, source{source_} {} + + Type type; + std::atomic_int refCount; + std::shared_ptr source; +}; + +typedef StaticUnlimitedHandleResource + Sources; + +struct SinkData { + enum Type { + kUnknown = 0, + kHTTP, + kCv + }; + explicit SinkData(Type type_, std::shared_ptr sink_) + : type{type_}, refCount{0}, sourceHandle{0}, sink{sink_} {} + + Type type; + std::atomic_int refCount; + std::atomic sourceHandle; + std::shared_ptr sink; +}; + +typedef StaticUnlimitedHandleResource Sinks; + +} // namespace cs + +#endif // CAMERASERVER_HANDLE_H_ diff --git a/src/Image.h b/src/Image.h new file mode 100644 index 0000000000..d0b3a9ed8c --- /dev/null +++ b/src/Image.h @@ -0,0 +1,55 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) FIRST 2016. 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 CAMERASERVER_IMAGE_H_ +#define CAMERASERVER_IMAGE_H_ + +#include + +namespace cs { + +class Image { + public: + enum Type { + kUnknown = 0, + kJpeg + }; + + Image() : m_data(nullptr), m_size(0), m_capacity(0) {} + explicit Image(std::size_t capacity) + : m_data(new char[capacity]), m_capacity(capacity) {} + Image(Image&& image) + : m_data(image.m_data), + m_size(image.m_size), + m_capacity(image.m_capacity) { + image.m_data = nullptr; + image.m_size = 0; + image.m_capacity = 0; + } + ~Image() { delete[] m_data; } + Image(const Image&) = delete; + Image& operator=(const Image&) = delete; + + char* data() { return m_data; } + const char* data() const { return m_data; } + std::size_t size() const { return m_size; } + std::size_t capacity() const { return m_capacity; } + Type type() const { return m_type; } + + void SetSize(std::size_t size) { m_size = size; } + void SetType(Type type) { m_type = type; } + + private: + char* m_data; + std::size_t m_size; + std::size_t m_capacity; + Type m_type = kUnknown; +}; + +} // namespace cs + +#endif // CAMERASERVER_IMAGE_H_ diff --git a/src/SinkImpl.cpp b/src/SinkImpl.cpp new file mode 100644 index 0000000000..f600392e06 --- /dev/null +++ b/src/SinkImpl.cpp @@ -0,0 +1,14 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) FIRST 2016. 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 "SinkImpl.h" + +#include "SourceImpl.h" + +using namespace cs; + +SinkImpl::SinkImpl(llvm::StringRef name) : m_name{name} {} diff --git a/src/SinkImpl.h b/src/SinkImpl.h new file mode 100644 index 0000000000..c6ecfa73f1 --- /dev/null +++ b/src/SinkImpl.h @@ -0,0 +1,50 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) FIRST 2016. 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 CAMERASERVER_SINKIMPL_H_ +#define CAMERASERVER_SINKIMPL_H_ + +#include +#include +#include + +#include "llvm/StringRef.h" + +namespace cs { + +class Frame; +class SourceImpl; + +class SinkImpl { + public: + SinkImpl(llvm::StringRef name); + virtual ~SinkImpl() = default; + SinkImpl(const SinkImpl& queue) = delete; + SinkImpl& operator=(const SinkImpl& queue) = delete; + + llvm::StringRef GetName() const { return m_name; } + virtual void GetDescription(llvm::SmallVectorImpl& desc) const = 0; + + void SetSource(std::shared_ptr source) { + std::lock_guard lock(m_mutex); + m_source = source; + } + + std::shared_ptr GetSource() const { + std::lock_guard lock(m_mutex); + return m_source; + } + + private: + std::string m_name; + mutable std::mutex m_mutex; + std::shared_ptr m_source; +}; + +} // namespace cs + +#endif // CAMERASERVER_SINKIMPL_H_ diff --git a/src/SourceImpl.cpp b/src/SourceImpl.cpp new file mode 100644 index 0000000000..427cf2fe13 --- /dev/null +++ b/src/SourceImpl.cpp @@ -0,0 +1,69 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) FIRST 2016. 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 "SourceImpl.h" + +#include "llvm/STLExtras.h" + +using namespace cs; + +SourceImpl::SourceImpl(llvm::StringRef name) + : m_name{name}, m_frame{*this, nullptr} {} + +SourceImpl::~SourceImpl() { + // Wake up anyone who is waiting. This also clears the current frame, + // which is good because its destructor will call back into the class. + Wakeup(); + // Everything else can clean up itself. +} + +Frame SourceImpl::GetNextFrame() { + std::unique_lock lock{m_frameMutex}; + // TODO: handle spurious wakeup by comparing frame timestamps + m_cv.wait(lock); + return m_frame; +} + +void SourceImpl::Wakeup() { + { + std::lock_guard lock{m_frameMutex}; + m_frame = Frame{*this, nullptr}; + } + m_cv.notify_all(); +} + +void SourceImpl::StartFrame() { + std::lock_guard lock{m_mutex}; + if (m_frameData) return; + if (m_framesAvail.empty()) { + m_frameData = llvm::make_unique(); + } else { + m_frameData = std::move(m_framesAvail.back()); + m_framesAvail.pop_back(); + m_frameData->refcount = 0; + } +} + +//TODO: Image& SourceImpl::AddImage(std::size_t channel, std::size_t size) {} + +void SourceImpl::FinishFrame() { + { + std::lock_guard lock{m_mutex}; + std::lock_guard lock2{m_frameMutex}; + m_frame = Frame{*this, m_frameData.release()}; + } + m_cv.notify_all(); +} + +void SourceImpl::ReleaseFrame(Frame::Data* data) { + std::lock_guard lock{m_mutex}; + // Return the images to the pool + for (auto&& image : data->images) + m_imagesAvail.emplace_back(std::move(image)); + // Return the frame to the pool + m_framesAvail.emplace_back(data); +} diff --git a/src/SourceImpl.h b/src/SourceImpl.h new file mode 100644 index 0000000000..9039a138d1 --- /dev/null +++ b/src/SourceImpl.h @@ -0,0 +1,99 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) FIRST 2016. 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 CAMERASERVER_SOURCEIMPL_H_ +#define CAMERASERVER_SOURCEIMPL_H_ + +#include +#include +#include +#include +#include +#include + +#include "llvm/StringRef.h" +#include "cameraserver_c.h" +#include "Frame.h" + +namespace cs { + +class SourceImpl { + friend class Frame; + + public: + SourceImpl(llvm::StringRef name); + virtual ~SourceImpl(); + SourceImpl(const SourceImpl& queue) = delete; + SourceImpl& operator=(const SourceImpl& queue) = delete; + + llvm::StringRef GetName() const { return m_name; } + virtual void GetDescription(llvm::SmallVectorImpl& desc) const = 0; + int GetNumChannels() const { return m_numChannels; } + bool IsConnected() const { return m_connected; } + + // Gets the current frame (without waiting for a new one). + Frame GetCurFrame(); + + // Blocking function that waits for the next frame and returns it. + Frame GetNextFrame(); + + // Force a wakeup of all GetNextFrame() callers by sending an empty frame. + void Wakeup(); + + // Property functions + virtual int GetProperty(llvm::StringRef name) const = 0; + virtual void EnumerateProperties( + llvm::SmallVectorImpl& properties) const = 0; + virtual CS_PropertyType GetPropertyType(int property) const = 0; + virtual void GetPropertyName(int property, + llvm::SmallVectorImpl& name) const = 0; + virtual bool GetBooleanProperty(int property) const = 0; + virtual void SetBooleanProperty(int property, bool value) = 0; + virtual double GetDoubleProperty(int property) const = 0; + virtual void SetDoubleProperty(int property, double value) = 0; + virtual double GetPropertyMin(int property) const = 0; + virtual double GetPropertyMax(int property) const = 0; + virtual void GetStringProperty(int property, + llvm::SmallVectorImpl& value) const = 0; + virtual void SetStringProperty(int property, llvm::StringRef value) = 0; + virtual int GetEnumProperty(int property) const = 0; + virtual void SetEnumProperty(int property, int value) = 0; + virtual std::vector GetEnumPropertyChoices( + int property) const = 0; + + protected: + void StartFrame(); + Image& AddImage(std::size_t channel, std::size_t size); + void FinishFrame(); + + std::atomic_int m_numChannels{0}; + std::atomic_bool m_connected{false}; + + private: + void ReleaseFrame(Frame::Data* data); + + std::string m_name; + + std::mutex m_mutex; + std::mutex m_frameMutex; + std::condition_variable m_cv; + + // Most recent complete frame (returned to callers of GetNextFrame) + // Access protected by m_frameMutex. + Frame m_frame; + + // In-progress frame. Will be moved to m_frame when complete. + std::unique_ptr m_frameData; + + // Pools of frames and images to reduce malloc traffic. + std::vector> m_framesAvail; + std::vector m_imagesAvail; +}; + +} // namespace cs + +#endif // CAMERASERVER_SOURCEIMPL_H_ diff --git a/src/UnlimitedHandleResource.h b/src/UnlimitedHandleResource.h new file mode 100644 index 0000000000..403998f5e1 --- /dev/null +++ b/src/UnlimitedHandleResource.h @@ -0,0 +1,154 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) FIRST 2016. 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 CAMERASERVER_UNLIMITEDHANDLERESOURCE_H_ +#define CAMERASERVER_UNLIMITEDHANDLERESOURCE_H_ + +#include +#include +#include + +#include "llvm/SmallVector.h" +#include "support/atomic_static.h" + +namespace cs { + +// The UnlimitedHandleResource class is a way to track handles. This version +// allows an unlimted number of handles that are allocated sequentially. When +// possible, indices are reused to save memory usage and keep the array length +// down. +// However, automatic array management has not been implemented, but might be in +// the future. +// Because we have to loop through the allocator, we must use a global mutex. +// +// THandle needs to have the following attributes: +// Type : enum or typedef +// kIndexMax : static, constexpr, or enum value for the maximum index value +// int GetTypedIndex() const : function that returns the index of the handle +// THandle(int index, HandleType[int] type) : constructor for index and type +// +// @tparam THandle The Handle Type +// @tparam TStruct The struct type held by this resource +// @tparam typeValue The type value stored in the handle +// @tparam TMutex The mutex type to use +template +class UnlimitedHandleResource { + public: + UnlimitedHandleResource(const UnlimitedHandleResource&) = delete; + UnlimitedHandleResource operator=(const UnlimitedHandleResource&) = delete; + UnlimitedHandleResource() = default; + + template + THandle Allocate(Args&&... args); + THandle Allocate(std::shared_ptr structure); + + std::shared_ptr Get(THandle handle); + + void Free(THandle handle); + + template + void GetAll(llvm::SmallVectorImpl& handles); + + private: + THandle MakeHandle(size_t i) { + return THandle{static_cast(i), + static_cast(typeValue)}; + } + std::vector> m_structures; + TMutex m_handleMutex; +}; + +template +template +THandle UnlimitedHandleResource::Allocate( + Args&&... args) { + std::lock_guard sync(m_handleMutex); + size_t i; + for (i = 0; i < m_structures.size(); i++) { + if (m_structures[i] == nullptr) { + m_structures[i] = std::make_shared(std::forward(args)...); + return MakeHandle(i); + } + } + if (i >= THandle::kIndexMax) return 0; + + m_structures.emplace_back( + std::make_shared(std::forward(args)...)); + return MakeHandle(i); +} + +template +THandle UnlimitedHandleResource::Allocate( + std::shared_ptr structure) { + std::lock_guard sync(m_handleMutex); + size_t i; + for (i = 0; i < m_structures.size(); i++) { + if (m_structures[i] == nullptr) { + m_structures[i] = structure; + return MakeHandle(i); + } + } + if (i >= THandle::kIndexMax) return 0; + + m_structures.push_back(structure); + return MakeHandle(i); +} + +template +std::shared_ptr UnlimitedHandleResource::Get(THandle handle) { + auto index = + handle.GetTypedIndex(static_cast(typeValue)); + if (index < 0) return nullptr; + std::lock_guard sync(m_handleMutex); + if (index >= static_cast(m_structures.size())) + return nullptr; + return m_structures[index]; +} + +template +void UnlimitedHandleResource::Free( + THandle handle) { + auto index = + handle.GetTypedIndex(static_cast(typeValue)); + if (index < 0) return; + std::lock_guard sync(m_handleMutex); + if (index >= static_cast(m_structures.size())) return; + m_structures[index].reset(); +} + +template +template +void UnlimitedHandleResource::GetAll( + llvm::SmallVectorImpl& handles) { + std::lock_guard sync(m_handleMutex); + size_t i; + for (i = 0; i < m_structures.size(); i++) { + if (m_structures[i] != nullptr) handles.push_back(MakeHandle(i)); + } +} + +template +class StaticUnlimitedHandleResource + : public UnlimitedHandleResource { + public: + static StaticUnlimitedHandleResource& GetInstance() { + ATOMIC_STATIC(StaticUnlimitedHandleResource, instance); + return instance; + } + + private: + StaticUnlimitedHandleResource() {} + + ATOMIC_STATIC_DECL(StaticUnlimitedHandleResource) +}; + +} // namespace cs + +#endif // CAMERASERVER_UNLIMITEDHANDLERESOURCE_H_ diff --git a/src/cameraserver_cpp.cpp b/src/cameraserver_cpp.cpp index 573e23c635..951a1e0fae 100644 --- a/src/cameraserver_cpp.cpp +++ b/src/cameraserver_cpp.cpp @@ -7,10 +7,32 @@ #include "cameraserver_cpp.h" -#include +#include "llvm/SmallString.h" + +#include "SinkImpl.h" +#include "SourceImpl.h" +#include "Handle.h" using namespace cs; +static std::shared_ptr GetPropertySource(CS_Property propertyHandle, + int* propertyIndex, + CS_Status* status) { + Handle handle{propertyHandle}; + int i = handle.GetTypedIndex(Handle::kProperty); + if (i < 0) { + *status = CS_INVALID_HANDLE; + return nullptr; + } + auto data = Sources::GetInstance().Get(Handle{i, Handle::kSource}); + if (!data) { + *status = CS_INVALID_HANDLE; + return nullptr; + } + *propertyIndex = handle.GetSubIndex(); + return data->source; +} + namespace cs { // @@ -18,67 +40,116 @@ namespace cs { // CS_PropertyType GetPropertyType(CS_Property property, CS_Status* status) { - return CS_PROP_NONE; // TODO + int propertyIndex; + auto source = GetPropertySource(property, &propertyIndex, status); + if (!source) return CS_PROP_NONE; + return source->GetPropertyType(propertyIndex); } std::string GetPropertyName(CS_Property property, CS_Status* status) { - return ""; // TODO + llvm::SmallString<128> name; + int propertyIndex; + auto source = GetPropertySource(property, &propertyIndex, status); + if (!source) return std::string{}; + source->GetPropertyName(propertyIndex, name); + return name.str(); } void GetPropertyName(CS_Property property, llvm::SmallVectorImpl& name, CS_Status* status) { - // TODO + int propertyIndex; + auto source = GetPropertySource(property, &propertyIndex, status); + if (!source) return; + source->GetPropertyName(propertyIndex, name); } bool GetBooleanProperty(CS_Property property, CS_Status* status) { - return false; // TODO + int propertyIndex; + auto source = GetPropertySource(property, &propertyIndex, status); + if (!source) return false; + return source->GetBooleanProperty(propertyIndex); } void SetBooleanProperty(CS_Property property, bool value, CS_Status* status) { - // TODO + int propertyIndex; + auto source = GetPropertySource(property, &propertyIndex, status); + if (!source) return; + source->SetBooleanProperty(propertyIndex, value); } double GetDoubleProperty(CS_Property property, CS_Status* status) { - return 0.0; // TODO + int propertyIndex; + auto source = GetPropertySource(property, &propertyIndex, status); + if (!source) return false; + return source->GetDoubleProperty(propertyIndex); } void SetDoubleProperty(CS_Property property, double value, CS_Status* status) { - // TODO + int propertyIndex; + auto source = GetPropertySource(property, &propertyIndex, status); + if (!source) return; + source->SetDoubleProperty(propertyIndex, value); } double GetDoublePropertyMin(CS_Property property, CS_Status* status) { - return 0.0; // TODO + int propertyIndex; + auto source = GetPropertySource(property, &propertyIndex, status); + if (!source) return 0.0; + return source->GetPropertyMin(propertyIndex); } double GetDoublePropertyMax(CS_Property property, CS_Status* status) { - return 0.0; // TODO + int propertyIndex; + auto source = GetPropertySource(property, &propertyIndex, status); + if (!source) return 0.0; + return source->GetPropertyMax(propertyIndex); } std::string GetStringProperty(CS_Property property, CS_Status* status) { - return ""; // TODO + llvm::SmallString<128> value; + int propertyIndex; + auto source = GetPropertySource(property, &propertyIndex, status); + if (!source) return std::string{}; + source->GetStringProperty(propertyIndex, value); + return value.str(); } void GetStringProperty(CS_Property property, llvm::SmallVectorImpl& value, CS_Status* status) { - // TODO + int propertyIndex; + auto source = GetPropertySource(property, &propertyIndex, status); + if (!source) return; + source->GetStringProperty(propertyIndex, value); } void SetStringProperty(CS_Property property, llvm::StringRef value, CS_Status* status) { - // TODO + int propertyIndex; + auto source = GetPropertySource(property, &propertyIndex, status); + if (!source) return; + source->SetStringProperty(propertyIndex, value); } int GetEnumProperty(CS_Property property, CS_Status* status) { - return 0; // TODO + int propertyIndex; + auto source = GetPropertySource(property, &propertyIndex, status); + if (!source) return 0; + return source->GetEnumProperty(propertyIndex); } void SetEnumProperty(CS_Property property, int value, CS_Status* status) { - // TODO + int propertyIndex; + auto source = GetPropertySource(property, &propertyIndex, status); + if (!source) return; + source->SetEnumProperty(propertyIndex, value); } std::vector GetEnumPropertyChoices(CS_Property property, CS_Status* status) { - return std::vector{}; // TODO + int propertyIndex; + auto source = GetPropertySource(property, &propertyIndex, status); + if (!source) return std::vector{}; + return source->GetEnumPropertyChoices(propertyIndex); } // @@ -109,21 +180,44 @@ CS_Source CreateCvSource(llvm::StringRef name, int numChannels, // std::string GetSourceName(CS_Source source, CS_Status* status) { - return ""; // TODO + auto data = Sources::GetInstance().Get(source); + if (!data) { + *status = CS_INVALID_HANDLE; + return std::string{}; + } + return data->source->GetName(); } -void GetSourceName(CS_Source sink, llvm::SmallVectorImpl& name, +void GetSourceName(CS_Source source, llvm::SmallVectorImpl& name, CS_Status* status) { - // TODO + auto data = Sources::GetInstance().Get(source); + if (!data) { + *status = CS_INVALID_HANDLE; + return; + } + auto str = data->source->GetName(); + name.append(str.begin(), str.end()); } std::string GetSourceDescription(CS_Source source, CS_Status* status) { - return ""; // TODO + llvm::SmallString<128> desc; + auto data = Sources::GetInstance().Get(source); + if (!data) { + *status = CS_INVALID_HANDLE; + return std::string{}; + } + data->source->GetDescription(desc); + return desc.str(); } void GetSourceDescription(CS_Source source, llvm::SmallVectorImpl& desc, CS_Status* status) { - // TODO + auto data = Sources::GetInstance().Get(source); + if (!data) { + *status = CS_INVALID_HANDLE; + return; + } + data->source->GetDescription(desc); } uint64_t GetSourceLastFrameTime(CS_Source source, CS_Status* status) { @@ -131,16 +225,36 @@ uint64_t GetSourceLastFrameTime(CS_Source source, CS_Status* status) { } int GetSourceNumChannels(CS_Source source, CS_Status* status) { - return 0; // TODO + auto data = Sources::GetInstance().Get(source); + if (!data) { + *status = CS_INVALID_HANDLE; + return 0; + } + return data->source->GetNumChannels(); } bool IsSourceConnected(CS_Source source, CS_Status* status) { - return false; // TODO + auto data = Sources::GetInstance().Get(source); + if (!data) { + *status = CS_INVALID_HANDLE; + return false; + } + return data->source->IsConnected(); } CS_Property GetSourceProperty(CS_Source source, llvm::StringRef name, CS_Status* status) { - return 0; // TODO + auto data = Sources::GetInstance().Get(source); + if (!data) { + *status = CS_INVALID_HANDLE; + return 0; + } + int property = data->source->GetProperty(name); + if (property < 0) { + *status = CS_INVALID_HANDLE; + return 0; + } + return Handle{source, property, Handle::kProperty}; } void EnumerateSourceProperties(CS_Source source, @@ -150,11 +264,25 @@ void EnumerateSourceProperties(CS_Source source, } CS_Source CopySource(CS_Source source, CS_Status* status) { - return source; // TODO + if (source == 0) return 0; + auto data = Sources::GetInstance().Get(source); + if (!data) { + *status = CS_INVALID_HANDLE; + return 0; + } + data->refCount++; + return source; } void ReleaseSource(CS_Source source, CS_Status* status) { - // TODO + if (source == 0) return; + auto& inst = Sources::GetInstance(); + auto data = inst.Get(source); + if (!data) { + *status = CS_INVALID_HANDLE; + return; + } + if (--(data->refCount) == 0) inst.Free(source); } // @@ -227,42 +355,100 @@ CS_Sink CreateCvSinkCallback(llvm::StringRef name, // Sink Functions // std::string GetSinkName(CS_Sink sink, CS_Status* status) { - return ""; // TODO + auto data = Sinks::GetInstance().Get(sink); + if (!data) { + *status = CS_INVALID_HANDLE; + return std::string{}; + } + return data->sink->GetName(); } void GetSinkName(CS_Sink sink, llvm::SmallVectorImpl& name, CS_Status* status) { - // TODO + auto data = Sinks::GetInstance().Get(sink); + if (!data) { + *status = CS_INVALID_HANDLE; + return; + } + auto str = data->sink->GetName(); + name.append(str.begin(), str.end()); } std::string GetSinkDescription(CS_Sink sink, CS_Status* status) { - return ""; // TODO + llvm::SmallString<128> desc; + auto data = Sinks::GetInstance().Get(sink); + if (!data) { + *status = CS_INVALID_HANDLE; + return std::string{}; + } + data->sink->GetDescription(desc); + return desc.str(); } void GetSinkDescription(CS_Sink sink, llvm::SmallVectorImpl& desc, CS_Status* status) { - // TODO + auto data = Sinks::GetInstance().Get(sink); + if (!data) { + *status = CS_INVALID_HANDLE; + return; + } + data->sink->GetDescription(desc); } void SetSinkSource(CS_Sink sink, CS_Source source, CS_Status* status) { - // TODO + auto data = Sinks::GetInstance().Get(sink); + if (!data) { + *status = CS_INVALID_HANDLE; + return; + } + auto sourceData = Sources::GetInstance().Get(source); + if (!sourceData) { + *status = CS_INVALID_HANDLE; + return; + } + data->sink->SetSource(sourceData->source); + data->sourceHandle.store(source); } CS_Source GetSinkSource(CS_Sink sink, CS_Status* status) { - return 0; // TODO + auto data = Sinks::GetInstance().Get(sink); + if (!data) { + *status = CS_INVALID_HANDLE; + return 0; + } + return data->sourceHandle.load(); } CS_Property GetSinkSourceProperty(CS_Sink sink, llvm::StringRef name, CS_Status* status) { - return 0; // TODO + auto data = Sinks::GetInstance().Get(sink); + if (!data) { + *status = CS_INVALID_HANDLE; + return 0; + } + return GetSourceProperty(data->sourceHandle.load(), name, status); } CS_Sink CopySink(CS_Sink sink, CS_Status* status) { - return sink; // TODO + if (sink == 0) return 0; + auto data = Sinks::GetInstance().Get(sink); + if (!data) { + *status = CS_INVALID_HANDLE; + return 0; + } + data->refCount++; + return sink; } void ReleaseSink(CS_Sink sink, CS_Status* status) { - // TODO + if (sink == 0) return; + auto& inst = Sinks::GetInstance(); + auto data = inst.Get(sink); + if (!data) { + *status = CS_INVALID_HANDLE; + return; + } + if (--(data->refCount) == 0) inst.Free(sink); } // @@ -338,12 +524,12 @@ std::vector EnumerateUSBCameras(CS_Status* status) { void EnumerateSourceHandles(llvm::SmallVectorImpl& handles, CS_Status* status) { - // TODO + Sources::GetInstance().GetAll(handles); } void EnumerateSinkHandles(llvm::SmallVectorImpl& handles, CS_Status* status) { - // TODO + Sinks::GetInstance().GetAll(handles); } } // namespace cs