From aec16a934f1aaef2889ef59b1aa323520b2a18a1 Mon Sep 17 00:00:00 2001 From: Peter Johnson Date: Thu, 25 Aug 2016 23:13:48 -0700 Subject: [PATCH] Revamp API again and start implementing C and Java wrapper shells. --- include/cameraserver_c.h | 172 +++++ include/cameraserver_cpp.h | 683 ++++++++++++++---- .../edu/wpi/cameraserver/CameraServerJNI.java | 115 ++- java/src/edu/wpi/cameraserver/CvSink.java | 61 ++ java/src/edu/wpi/cameraserver/CvSource.java | 68 ++ java/src/edu/wpi/cameraserver/HTTPCamera.java | 11 + java/src/edu/wpi/cameraserver/HTTPSink.java | 27 + java/src/edu/wpi/cameraserver/USBCamera.java | 24 + .../edu/wpi/cameraserver/USBCameraInfo.java | 21 + java/src/edu/wpi/cameraserver/VideoSink.java | 68 ++ .../src/edu/wpi/cameraserver/VideoSource.java | 66 ++ src/cameraserver_c.cpp | 315 ++++++++ src/cameraserver_cpp.cpp | 362 +++++++--- 13 files changed, 1720 insertions(+), 273 deletions(-) create mode 100644 java/src/edu/wpi/cameraserver/CvSink.java create mode 100644 java/src/edu/wpi/cameraserver/CvSource.java create mode 100644 java/src/edu/wpi/cameraserver/HTTPCamera.java create mode 100644 java/src/edu/wpi/cameraserver/HTTPSink.java create mode 100644 java/src/edu/wpi/cameraserver/USBCamera.java create mode 100644 java/src/edu/wpi/cameraserver/USBCameraInfo.java create mode 100644 java/src/edu/wpi/cameraserver/VideoSink.java create mode 100644 java/src/edu/wpi/cameraserver/VideoSource.java create mode 100644 src/cameraserver_c.cpp diff --git a/include/cameraserver_c.h b/include/cameraserver_c.h index df7cb94dae..4246db9f51 100644 --- a/include/cameraserver_c.h +++ b/include/cameraserver_c.h @@ -9,11 +9,28 @@ #define CAMERASERVER_C_H_ #include +#include #ifdef __cplusplus extern "C" { #endif +struct CvMat; + +// +// Typedefs +// +typedef int CS_Bool; +typedef int CS_Property; +typedef int CS_Listener; +typedef int CS_Sink; +typedef int CS_Source; +typedef int CS_Status; + +// +// Property Functions +// + enum CS_PropertyType { CS_PROP_NONE = 0, CS_PROP_BOOLEAN, @@ -22,6 +39,161 @@ enum CS_PropertyType { CS_PROP_ENUM }; +enum CS_PropertyType CS_GetPropertyType(CS_Property property, + CS_Status* status); +CS_Bool CS_GetBooleanProperty(CS_Property property, CS_Status* status); +void CS_SetBooleanProperty(CS_Property property, CS_Bool value, + CS_Status* status); +double CS_GetDoubleProperty(CS_Property property, CS_Status* status); +void CS_SetDoubleProperty(CS_Property property, double value, + CS_Status* status); +double CS_GetDoublePropertyMin(CS_Property property, CS_Status* status); +double CS_GetDoublePropertyMax(CS_Property property, CS_Status* status); +char* CS_GetStringProperty(CS_Property property, CS_Status* status); +void CS_SetStringProperty(CS_Property property, const char* value, + CS_Status* status); +int CS_GetEnumProperty(CS_Property property, CS_Status* status); +void CS_SetEnumProperty(CS_Property property, int value, CS_Status* status); +char** CS_GetEnumPropertyChoices(CS_Property property, int* count, + CS_Status* status); + +// +// Source/Sink Functions +// +CS_Property CS_GetSourceProperty(CS_Source handle, const char* name, + CS_Status* status); + +// +// Source Creation Functions +// +CS_Source CS_CreateUSBSourceDev(const char* name, int dev, CS_Status* status); +CS_Source CS_CreateUSBSourcePath(const char* name, const char* path, + CS_Status* status); +CS_Source CS_CreateHTTPSource(const char* name, const char* url, + CS_Status* status); +CS_Source CS_CreateCvSource(const char* name, int numChannels, + CS_Status* status); + +// +// Source Functions +// +char* CS_GetSourceName(CS_Source source, CS_Status* status); +char* CS_GetSourceDescription(CS_Source source, CS_Status* status); +uint64_t CS_GetSourceLastFrameTime(CS_Source source, CS_Status* status); +int CS_GetSourceNumChannels(CS_Source source, CS_Status* status); +CS_Bool CS_IsSourceConnected(CS_Source source, CS_Status* status); +CS_Source CS_CopySource(CS_Source source, CS_Status* status); +void CS_ReleaseSource(CS_Source source, CS_Status* status); + +// +// OpenCV Source Functions +// +void CS_PutSourceImage(CS_Source source, int channel, struct CvMat* image, + CS_Status* status); +void CS_NotifySourceFrame(CS_Source source, CS_Status* status); +void CS_PutSourceFrame(CS_Source source, struct CvMat* image, + CS_Status* status); +void CS_NotifySourceError(CS_Source source, const char* msg, CS_Status* status); +void CS_SetSourceConnected(CS_Source source, CS_Bool connected, + CS_Status* status); +CS_Property CS_CreateSourceProperty(CS_Source source, const char* name, + enum CS_PropertyType type, + CS_Status* status); +CS_Property CS_CreateSourcePropertyCallback( + CS_Source source, const char* name, enum CS_PropertyType type, void* data, + void (*onChange)(void* data, const char* name, CS_Property property), + CS_Status* status); +void CS_RemoveSourceProperty(CS_Source source, const char* name, + CS_Status* status); + +// +// Sink Creation Functions +// +CS_Sink CS_CreateHTTPSink(const char* name, const char* listenAddress, int port, + CS_Status* status); +CS_Sink CS_CreateCvSink(const char* name, CS_Status* status); +CS_Sink CS_CreateCvSinkCallback(const char* name, void* data, + void (*processFrame)(void* data, uint64_t time), + CS_Status* status); + +// +// Sink Functions +// +char* CS_GetSinkName(CS_Sink sink, CS_Status* status); +char* CS_GetSinkDescription(CS_Sink sink, CS_Status* status); +void CS_SetSinkSource(CS_Sink sink, CS_Source source, CS_Status* status); +CS_Source CS_GetSinkSource(CS_Sink sink, CS_Status* status); +CS_Sink CS_CopySink(CS_Sink sink, CS_Status* status); +void CS_ReleaseSink(CS_Sink sink, CS_Status* status); + +// +// Server Sink (e.g. HTTP) Functions +// +void CS_SetSinkSourceChannel(CS_Sink sink, int channel, CS_Status* status); + +// +// OpenCV Sink Functions +// +uint64_t CS_SinkWaitForFrame(CS_Sink sink, CS_Status* status); +CS_Bool CS_GetSinkImage(CS_Sink sink, int channel, struct CvMat* image, + CS_Status* status); +uint64_t CS_GrabSinkFrame(CS_Sink sink, struct CvMat* image, CS_Status* status); +char* CS_GetSinkError(CS_Sink sink, CS_Status* status); +void CS_SetSinkEnabled(CS_Sink sink, CS_Bool enabled, CS_Status* status); + +// +// Listener Functions +// +enum CS_SourceEvent { + CS_SOURCE_CREATED = 0x01, + CS_SOURCE_DESTROYED = 0x02, + CS_SOURCE_CONNECTED = 0x04, + CS_SOURCE_DISCONNECTED = 0x08 +}; + +CS_Listener CS_AddSourceListener(void* data, + void (*callback)(void* data, const char* name, + CS_Source source, int event), + int eventMask, CS_Status* status); + +void CS_RemoveSourceListener(CS_Listener handle, CS_Status* status); + +enum CS_SinkEvent { + CS_SINK_CREATED = 0x01, + CS_SINK_DESTROYED = 0x02, + CS_SINK_ENABLED = 0x04, + CS_SINK_DISABLED = 0x08 +}; + +CS_Listener CS_AddSinkListener(void* data, + void (*callback)(void* data, const char* name, + CS_Sink sink, int event), + int eventMask, CS_Status* status); + +void CS_RemoveSinkListener(CS_Listener handle, CS_Status* status); + +// +// Utility Functions +// +typedef struct CS_USBCameraInfo { + int dev; + char* path; + char* name; + int channels; +} CS_USBCameraInfo; + +CS_USBCameraInfo* CS_EnumerateUSBCameras(int* count, CS_Status* status); +void CS_FreeEnumeratedUSBCameras(CS_USBCameraInfo* cameras); + +CS_Source* CS_EnumerateSources(int* count, CS_Status* status); +void CS_FreeEnumeratedSources(CS_Source* sources); + +CS_Sink* CS_EnumerateSinks(int* count, CS_Status* status); +void CS_FreeEnumeratedSinks(CS_Sink* sinks); + +void CS_FreeString(char* str); +void CS_FreeEnumPropertyChoices(char** choices); + #ifdef __cplusplus } #endif diff --git a/include/cameraserver_cpp.h b/include/cameraserver_cpp.h index 69ab559172..a0a248ef81 100644 --- a/include/cameraserver_cpp.h +++ b/include/cameraserver_cpp.h @@ -9,11 +9,12 @@ #define CAMERASERVER_CPP_H_ #include -#include #include +#include #include +#include "llvm/SmallVector.h" #include "llvm/StringRef.h" #include "cameraserver_c.h" @@ -24,8 +25,163 @@ class Mat; namespace cs { -class Property { - struct PrivateInit {}; +// +// Handle-based interface for C++. Users are encouraged to use the +// object oriented interface instead; this interface is intended for use +// in applications such as JNI which require handle-based access. +// + +// +// Property Functions +// + +CS_PropertyType GetPropertyType(CS_Property property, CS_Status* status); +bool GetBooleanProperty(CS_Property property, CS_Status* status); +void SetBooleanProperty(CS_Property property, bool value, CS_Status* status); +double GetDoubleProperty(CS_Property property, CS_Status* status); +void SetDoubleProperty(CS_Property property, double value, CS_Status* status); +double GetDoublePropertyMin(CS_Property property, CS_Status* status); +double GetDoublePropertyMax(CS_Property property, CS_Status* status); +std::string GetStringProperty(CS_Property property, CS_Status* status); +void GetStringProperty(CS_Property property, llvm::SmallVectorImpl& value, + CS_Status* status); +void SetStringProperty(CS_Property property, llvm::StringRef value, + CS_Status* status); +int GetEnumProperty(CS_Property property, CS_Status* status); +void SetEnumProperty(CS_Property property, int value, CS_Status* status); +std::vector GetEnumPropertyChoices(CS_Property property, + CS_Status* status); + +// +// Source/Sink Functions +// +CS_Property GetSourceProperty(CS_Source handle, llvm::StringRef name, + CS_Status* status); + +// +// Source Creation Functions +// +CS_Source CreateUSBSourceDev(llvm::StringRef name, int dev, CS_Status* status); +CS_Source CreateUSBSourcePath(llvm::StringRef name, llvm::StringRef path, + CS_Status* status); +CS_Source CreateHTTPSource(llvm::StringRef name, llvm::StringRef url, + CS_Status* status); +CS_Source CreateCvSource(llvm::StringRef name, int numChannels, + CS_Status* status); + +// +// Source Functions +// +std::string GetSourceName(CS_Source source, CS_Status* status); +void GetSourceName(CS_Source source, llvm::SmallVectorImpl& name, + CS_Status* status); +std::string GetSourceDescription(CS_Source source, CS_Status* status); +void GetSourceDescription(CS_Source source, llvm::SmallVectorImpl& desc, + CS_Status* status); +uint64_t GetSourceLastFrameTime(CS_Source source, CS_Status* status); +int GetSourceNumChannels(CS_Source source, CS_Status* status); +bool IsSourceConnected(CS_Source source, CS_Status* status); +CS_Source CopySource(CS_Source source, CS_Status* status); +void ReleaseSource(CS_Source source, CS_Status* status); + +// +// OpenCV Source Functions +// +void PutSourceImage(CS_Source source, int channel, cv::Mat* image, + CS_Status* status); +void NotifySourceFrame(CS_Source source, CS_Status* status); +void PutSourceFrame(CS_Source source, cv::Mat* image, CS_Status* status); +void NotifySourceError(CS_Source source, llvm::StringRef msg, + CS_Status* status); +void SetSourceConnected(CS_Source source, bool connected, CS_Status* status); +CS_Property CreateSourceProperty(CS_Source source, llvm::StringRef name, + CS_PropertyType type, CS_Status* status); +CS_Property CreateSourcePropertyCallback( + CS_Source source, llvm::StringRef name, CS_PropertyType type, + std::function onChange, + CS_Status* status); +void RemoveSourceProperty(CS_Source source, llvm::StringRef name, + CS_Status* status); + +// +// Sink Creation Functions +// +CS_Sink CreateHTTPSink(llvm::StringRef name, llvm::StringRef listenAddress, + int port, CS_Status* status); +CS_Sink CreateCvSink(llvm::StringRef name, CS_Status* status); +CS_Sink CreateCvSinkCallback(llvm::StringRef name, + std::function processFrame, + CS_Status* status); + +// +// Sink Functions +// +std::string GetSinkName(CS_Sink sink, CS_Status* status); +void GetSinkName(CS_Sink sink, llvm::SmallVectorImpl& name, + CS_Status* status); +std::string GetSinkDescription(CS_Sink sink, CS_Status* status); +void GetSinkDescription(CS_Sink sink, llvm::SmallVectorImpl& desc, + CS_Status* status); +void SetSinkSource(CS_Sink sink, CS_Source source, CS_Status* status); +CS_Source GetSinkSource(CS_Sink sink, CS_Status* status); +CS_Sink CopySink(CS_Sink sink, CS_Status* status); +void ReleaseSink(CS_Sink sink, CS_Status* status); + +// +// Server Sink (e.g. HTTP) Functions +// +void SetSinkSourceChannel(CS_Sink sink, int channel, CS_Status* status); + +// +// OpenCV Sink Functions +// +uint64_t SinkWaitForFrame(CS_Sink sink, CS_Status* status); +bool GetSinkImage(CS_Sink sink, int channel, cv::Mat* image, CS_Status* status); +uint64_t GrabSinkFrame(CS_Sink sink, cv::Mat* image, CS_Status* status); +std::string GetSinkError(CS_Sink sink, CS_Status* status); +void GetSinkError(CS_Sink sink, llvm::SmallVectorImpl msg, + CS_Status* status); +void SetSinkEnabled(CS_Sink sink, bool enabled, CS_Status* status); + +// +// Listener Functions +// +CS_Listener AddSourceListener( + std::function + callback, + int eventMask, CS_Status* status); + +void RemoveSourceListener(CS_Listener handle, CS_Status* status); + +CS_Listener AddSinkListener( + std::function callback, + int eventMask, CS_Status* status); + +void RemoveSinkListener(CS_Listener handle, CS_Status* status); + +// +// Utility Functions +// +void EnumerateSourceHandles(llvm::SmallVectorImpl& handles, + CS_Status* status); +void EnumerateSinkHandles(llvm::SmallVectorImpl& handles, + CS_Status* status); + +// +// Object-oriented interface +// + +// Forward declarations so friend declarations work correctly +class CvSource; +class SinkListener; +class SourceListener; +class VideoSink; +class VideoSource; + +class VideoProperty { + friend class CvSource; + friend class VideoSink; + friend class VideoSource; public: enum Type { @@ -36,9 +192,7 @@ class Property { kEnum = CS_PROP_ENUM }; - Property() : m_handle(0), m_property(0), m_type(kNone) {} - Property(int handle, int property, Type type, const PrivateInit&) : - m_handle(handle), m_property(property), m_type(type) {} + VideoProperty() : m_handle(0), m_type(kNone) {} Type type() const { return m_type; } @@ -51,27 +205,75 @@ class Property { bool IsEnum() const { return m_type == kEnum; } // Boolean-specific functions - bool GetBoolean() const; - void SetBoolean(bool value); + bool GetBoolean() const { + m_status = 0; + return GetBooleanProperty(m_handle, &m_status); + } + void SetBoolean(bool value) { + m_status = 0; + SetBooleanProperty(m_handle, value, &m_status); + } // Double-specific functions - double GetDouble() const; - void SetDouble(double value); - double GetMin() const; - double GetMax() const; + double GetDouble() const { + m_status = 0; + return GetDoubleProperty(m_handle, &m_status); + } + void SetDouble(double value) { + m_status = 0; + SetDoubleProperty(m_handle, value, &m_status); + } + double GetMin() const { + m_status = 0; + return GetDoublePropertyMin(m_handle, &m_status); + } + double GetMax() const { + m_status = 0; + return GetDoublePropertyMax(m_handle, &m_status); + } // String-specific functions - std::string GetString() const; - void SetString(llvm::StringRef value); + std::string GetString() const { + m_status = 0; + return GetStringProperty(m_handle, &m_status); + } + void GetString(llvm::SmallVectorImpl& value) const { + m_status = 0; + GetStringProperty(m_handle, value, &m_status); + } + void SetString(llvm::StringRef value) { + m_status = 0; + SetStringProperty(m_handle, value, &m_status); + } // Enum-specific functions - int GetEnum() const; - void SetEnum(int value); - std::vector GetChoices() const; + int GetEnum() const { + m_status = 0; + return GetEnumProperty(m_handle, &m_status); + } + void SetEnum(int value) { + m_status = 0; + SetEnumProperty(m_handle, value, &m_status); + } + std::vector GetChoices() const { + m_status = 0; + return GetEnumPropertyChoices(m_handle, &m_status); + } + + CS_Status GetLastStatus() const { return m_status; } private: - int m_handle; - int m_property; + explicit VideoProperty(CS_Property handle) : m_handle(handle) { + m_status = 0; + if (handle == 0) + m_type = kNone; + else + m_type = static_cast( + static_cast(GetPropertyType(handle, &m_status))); + } + + mutable CS_Status m_status; + CS_Property m_handle; Type m_type; }; @@ -79,45 +281,85 @@ class Property { /// consist of multiple images (e.g. from a stereo or depth camera); these /// are called channels. class VideoSource { + friend class SourceListener; + friend class VideoSink; + public: - VideoSource(const VideoSource&) = delete; - VideoSource& operator=(const VideoSource&) = delete; - ~VideoSource(); + VideoSource() : m_handle(0) {} + + VideoSource(const VideoSource& source) + : m_handle(source.m_handle == 0 ? 0 : CopySource(source.m_handle, + &m_status)) {} + + VideoSource(VideoSource&& source) noexcept : m_handle(source.m_handle) { + source.m_handle = 0; + } + + VideoSource& operator=(const VideoSource& rhs) { + m_status = 0; + if (m_handle != 0) ReleaseSource(m_handle, &m_status); + m_handle = rhs.m_handle == 0 ? 0 : CopySource(rhs.m_handle, &m_status); + return *this; + } + + ~VideoSource() { + m_status = 0; + if (m_handle != 0) ReleaseSource(m_handle, &m_status); + } + + explicit operator bool() const { return m_handle != 0; } /// Get the name of the source. The name is an arbitrary identifier /// provided when the source is created, and should be unique. - llvm::StringRef GetName() const { return m_name; } + llvm::StringRef GetName() const { + m_status = 0; + return GetSourceName(m_handle, &m_status); + } /// Get the source description. This is source-type specific. - std::string GetDescription() const; + std::string GetDescription() const { + m_status = 0; + return GetSourceDescription(m_handle, &m_status); + } /// Get the last time a frame was captured. - uint64_t GetLastFrameTime() const; + uint64_t GetLastFrameTime() const { + m_status = 0; + return GetSourceLastFrameTime(m_handle, &m_status); + } /// Get the number of channels this source provides. - int GetNumChannels() const; + int GetNumChannels() const { + m_status = 0; + return GetSourceNumChannels(m_handle, &m_status); + } /// Is the source currently connected to whatever is providing the images? - bool IsConnected() const; + bool IsConnected() const { + m_status = 0; + return IsSourceConnected(m_handle, &m_status); + } /// Get a property. /// @param name Property name /// @return Property contents (of type Property::kNone if no property with /// the given name exists) - Property GetProperty(llvm::StringRef name); + VideoProperty GetProperty(llvm::StringRef name) { + m_status = 0; + return VideoProperty{GetSourceProperty(m_handle, name, &m_status)}; + } + + CS_Status GetLastStatus() const { return m_status; } /// Enumerate all existing sources. /// @return Vector of sources. - static std::vector> EnumerateSources(); + static std::vector EnumerateSources(); protected: - struct PrivateInit {}; - VideoSource(llvm::StringRef name, unsigned int handle, const PrivateInit&) - : m_name(name), m_handle(handle) {} + explicit VideoSource(CS_Source handle) : m_handle(handle) {} - private: - std::string m_name; - int m_handle; + mutable CS_Status m_status = 0; + CS_Source m_handle; }; /// USB camera information @@ -133,201 +375,241 @@ struct USBCameraInfo { int channels; }; -/// A source that represents a video camera. -class CameraSource : public VideoSource { +/// A source that represents a USB camera. +class USBCamera : public VideoSource { public: /// Create a source for a USB camera based on device number. /// @param name Source name (arbitrary unique identifier) /// @param dev Device number (e.g. 0 for /dev/video0) - static std::shared_ptr CreateUSB(llvm::StringRef name, int dev); + USBCamera(llvm::StringRef name, int dev) { + m_handle = CreateUSBSourceDev(name, dev, &m_status); + } /// Create a source for a USB camera based on device path. /// @param name Source name (arbitrary unique identifier) /// @param path Path to device (e.g. "/dev/video0" on Linux) - static std::shared_ptr CreateUSB(llvm::StringRef name, - llvm::StringRef path); - - /// Create a source for a MJPEG-over-HTTP (IP) camera. - /// @param name Source name (arbitrary unique identifier) - /// @param url Camera URL (e.g. "http://10.x.y.11/video/stream.mjpg") - static std::shared_ptr CreateHTTP(llvm::StringRef name, - llvm::StringRef url); + USBCamera(llvm::StringRef name, llvm::StringRef path) { + m_handle = CreateUSBSourcePath(name, path, &m_status); + } /// Enumerate USB cameras on the local system. /// @return Vector of USB camera information (one for each camera) static std::vector EnumerateUSBCameras(); +}; - public: // can't be private due to internal use of std::make_shared<> - CameraSource(llvm::StringRef name, unsigned int handle, - const PrivateInit& init) - : VideoSource(name, handle, init) {} +/// A source that represents a MJPEG-over-HTTP (IP) camera. +class HTTPCamera : public VideoSource { + /// Create a source for a MJPEG-over-HTTP (IP) camera. + /// @param name Source name (arbitrary unique identifier) + /// @param url Camera URL (e.g. "http://10.x.y.11/video/stream.mjpg") + HTTPCamera(llvm::StringRef name, llvm::StringRef url) { + m_handle = CreateHTTPSource(name, url, &m_status); + } }; /// A source for user code to provide OpenCV images as video frames. class CvSource : public VideoSource { public: + /// Create an OpenCV source. + /// @param name Source name (arbitrary unique identifier) + /// @param numChannels Number of channels + CvSource(llvm::StringRef name, int numChannels = 1) { + m_handle = CreateCvSource(name, numChannels, &m_status); + } + /// Put an OpenCV image onto the specified channel. - /// @param channel Channel number (range 0 to nChannels-1) + /// @param channel Channel number (range 0 to numChannels-1) /// @param image OpenCV image - void PutImage(int channel, cv::Mat* image); + void PutImage(int channel, cv::Mat* image) { + m_status = 0; + PutSourceImage(m_handle, channel, image, &m_status); + } /// Signal sinks connected to this source that all new channel images have /// been put to the stream and are ready to be read. - void NotifyFrame(); + void NotifyFrame() { + m_status = 0; + NotifySourceFrame(m_handle, &m_status); + } /// Put an OpenCV image onto channel 0 and notify sinks. /// This is identical in behavior to calling PutImage(0, image) followed by /// NotifyFrame(). /// @param image OpenCV image - void PutFrame(cv::Mat* image); + void PutFrame(cv::Mat* image) { + m_status = 0; + PutSourceFrame(m_handle, image, &m_status); + } /// Signal sinks that an error has occurred. This should be called instead /// of NotifyFrame when an error occurs. - void NotifyError(llvm::StringRef msg); + void NotifyError(llvm::StringRef msg) { + m_status = 0; + NotifySourceError(m_handle, msg, &m_status); + } /// Set source connection status. Defaults to true. /// @param connected True for connected, false for disconnected - void SetConnected(bool connected); + void SetConnected(bool connected) { + m_status = 0; + SetSourceConnected(m_handle, connected, &m_status); + } /// Create a property. /// @param name Property name /// @param type Property type /// @return Property - Property CreateProperty(llvm::StringRef name, Property::Type type); + VideoProperty CreateProperty(llvm::StringRef name, VideoProperty::Type type) { + m_status = 0; + return VideoProperty{CreateSourceProperty( + m_handle, name, static_cast(static_cast(type)), + &m_status)}; + } /// Create a property with a change callback. /// @param name Property name /// @param type Property type /// @param onChange Callback to call when the property value changes /// @return Property - Property CreateProperty(llvm::StringRef name, Property::Type type, - std::function onChange); + VideoProperty CreateProperty( + llvm::StringRef name, VideoProperty::Type type, + std::function + onChange) { + m_status = 0; + return VideoProperty{CreateSourcePropertyCallback( + m_handle, name, static_cast(static_cast(type)), + [=](llvm::StringRef name, CS_Property property) { + onChange(name, VideoProperty{property}); + }, + &m_status)}; + } /// Remove a property. /// @param name Property name - void RemoveProperty(llvm::StringRef name); - - /// Create an OpenCV source. - /// @param name Source name (arbitrary unique identifier) - /// @param nChannels Number of channels - static std::shared_ptr Create(llvm::StringRef name, - int nChannels = 1); - - public: // can't be private due to internal use of std::make_shared<> - CvSource(llvm::StringRef name, unsigned int handle, const PrivateInit& init) - : VideoSource(name, handle, init) {} + void RemoveProperty(llvm::StringRef name) { + m_status = 0; + RemoveSourceProperty(m_handle, name, &m_status); + } }; /// A sink for video that accepts a sequence of frames. Each frame may /// consist of multiple images (e.g. from a stereo or depth camera); these are /// called channels. class VideoSink { + friend class SinkListener; + public: - VideoSink(const VideoSink&) = delete; - VideoSink& operator=(const VideoSink&) = delete; - ~VideoSink(); + VideoSink() : m_handle(0) {} + + VideoSink(const VideoSink& sink) + : m_handle(sink.m_handle == 0 ? 0 : CopySink(sink.m_handle, &m_status)) {} + + VideoSink(VideoSink&& sink) noexcept : m_handle(sink.m_handle) { + sink.m_handle = 0; + } + + VideoSink& operator=(const VideoSink& rhs) { + m_status = 0; + if (m_handle != 0) ReleaseSink(m_handle, &m_status); + m_handle = rhs.m_handle == 0 ? 0 : CopySink(rhs.m_handle, &m_status); + return *this; + } + + ~VideoSink() { + m_status = 0; + if (m_handle != 0) ReleaseSink(m_handle, &m_status); + } + + explicit operator bool() const { return m_handle != 0; } /// Get the name of the sink. The name is an arbitrary identifier /// provided when the sink is created, and should be unique. - llvm::StringRef GetName() const { return m_name; } + llvm::StringRef GetName() const { + m_status = 0; + return GetSinkName(m_handle, &m_status); + } /// Get the sink description. This is sink-type specific. - std::string GetDescription() const; + std::string GetDescription() const { + m_status = 0; + return GetSinkDescription(m_handle, &m_status); + } /// 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. - void SetSource(std::shared_ptr source); + /// @param source Source + void SetSource(VideoSource source) { + m_status = 0; + if (!source) + SetSinkSource(m_handle, 0, &m_status); + else + SetSinkSource(m_handle, source.m_handle, &m_status); + } + + /// Get the connected source. + /// @return Connected source (empty if none connected). + VideoSource GetSource() const { + m_status = 0; + return VideoSource{GetSinkSource(m_handle, &m_status)}; + } /// Get a property of the associated source. /// @param name Property name - /// @return Property contents (of type Property::kNone if no property with - /// the given name exists) - Property GetSourceProperty(llvm::StringRef name); + /// @return Property (type Property::kNone if no property with + /// the given name exists or no source connected) + VideoProperty GetSourceProperty(llvm::StringRef name) { + m_status = 0; + return VideoProperty{::cs::GetSourceProperty(m_handle, name, &m_status)}; + } + + CS_Status GetLastStatus() const { return m_status; } /// Enumerate all existing sinks. /// @return Vector of sinks. - static std::vector> EnumerateSinks(); + static std::vector EnumerateSinks(); protected: - struct PrivateInit {}; - VideoSink(llvm::StringRef name, unsigned int handle, const PrivateInit&) - : m_name(name), m_handle(handle) {} + explicit VideoSink(CS_Sink handle) : m_handle(handle) {} - private: - std::string m_name; - int m_handle; + mutable CS_Status m_status = 0; + CS_Sink m_handle; }; -/// A sink that acts as a network server. -class ServerSink : public VideoSink { +/// A sink that acts as a MJPEG-over-HTTP network server. +class HTTPSink : public VideoSink { public: - /// Set what video channel should be served. - /// Servers such as MJPEG-HTTP can only serve a single channel of video. - /// By default, channel 0 is served. - /// @param channel video channel to serve to clients - void SetSourceChannel(int channel); - /// Create a MJPEG-over-HTTP server sink. /// @param name Sink name (arbitrary unique identifier) /// @param listenAddress TCP listen address (empty string for all addresses) /// @param port TCP port number - static std::shared_ptr CreateHTTP(llvm::StringRef name, - llvm::StringRef listenAddress, - int port); + HTTPSink(llvm::StringRef name, llvm::StringRef listenAddress, int port) { + m_handle = CreateHTTPSink(name, listenAddress, port, &m_status); + } /// Create a MJPEG-over-HTTP server sink. /// @param name Sink name (arbitrary unique identifier) /// @param port TCP port number - static std::shared_ptr CreateHTTP(llvm::StringRef name, - int port) { - return CreateHTTP(name, "", port); + HTTPSink(llvm::StringRef name, int port) : HTTPSink(name, "", port) {} + + /// Set what video channel should be served. + /// MJPEG-HTTP can only serve a single channel of video. + /// By default, channel 0 is served. + /// @param channel video channel to serve to clients + void SetSourceChannel(int channel) { + m_status = 0; + SetSinkSourceChannel(m_handle, channel, &m_status); } - - //static std::shared_ptr CreateRTSP(llvm::StringRef name, - // llvm::StringRef listenAddress, - // int port); - - public: // can't be private due to internal use of std::make_shared<> - ServerSink(llvm::StringRef name, unsigned int handle, - const PrivateInit& init) - : VideoSink(name, handle, init) {} }; /// A sink for user code to accept video frames as OpenCV images. class CvSink : public VideoSink { public: - /// Wait for the next frame. This is a blocking call. - /// @return Frame time, or 0 on error (call GetError() to obtain the error - /// message). - uint64_t WaitForFrame() const; - - /// Get an OpenCV image from the specified channel. - /// @return False if image could not be obtained for some reason (e.g. - /// channel out of range) - bool GetImage(int channel, cv::Mat* image) const; - - /// Wait for the next frame and get the image from channel 0. Equivalent - /// to calling WaitForFrame() followed by GetImage(0, image). - /// @return Frame time, or 0 on error (call GetError() to obtain the error - /// message); - uint64_t FrameGrab(cv::Mat* image) const; - - /// Get error string. Call this if WaitForFrame() returns 0 to determine - /// what the error is. - std::string GetError() const; - - /// Enable or disable getting new frames. - /// Disabling will cause processFrame (for callback-based CvSinks) to not - /// be called and WaitForFrame() to not return. This can be used to save - /// processor resources when frames are not needed. - void SetEnabled(bool enabled); - /// Create a sink for accepting OpenCV images. /// WaitForFrame() must be called on the created sink to get each new /// image. /// @param name Source name (arbitrary unique identifier) - static std::shared_ptr Create(llvm::StringRef name); + CvSink(llvm::StringRef name) { m_handle = CreateCvSink(name, &m_status); } /// Create a sink for accepting OpenCV images in a separate thread. /// A thread will be created that calls WaitForFrame() and calls the @@ -337,27 +619,126 @@ class CvSink : public VideoSink { /// time=0 if an error occurred. processFrame should call GetImage() /// or GetError() as needed, but should not call (except in very /// unusual circumstances) WaitForImage(). - static std::shared_ptr Create( - llvm::StringRef name, std::function processFrame); + CvSink(llvm::StringRef name, + std::function processFrame) { + m_handle = CreateCvSinkCallback(name, processFrame, &m_status); + } - public: // can't be private due to internal use of std::make_shared<> - CvSink(llvm::StringRef name, unsigned int handle, const PrivateInit& init) - : VideoSink(name, handle, init) {} + /// Wait for the next frame. This is a blocking call. + /// @return Frame time, or 0 on error (call GetError() to obtain the error + /// message). + uint64_t WaitForFrame() const { + m_status = 0; + return SinkWaitForFrame(m_handle, &m_status); + } + + /// Get an OpenCV image from the specified channel. + /// @return False if image could not be obtained for some reason (e.g. + /// channel out of range) + bool GetImage(int channel, cv::Mat* image) const { + m_status = 0; + return GetSinkImage(m_handle, channel, image, &m_status); + } + + /// Wait for the next frame and get the image from channel 0. Equivalent + /// to calling WaitForFrame() followed by GetImage(0, image). + /// @return Frame time, or 0 on error (call GetError() to obtain the error + /// message); + uint64_t FrameGrab(cv::Mat* image) const { + m_status = 0; + return GrabSinkFrame(m_handle, image, &m_status); + } + + /// Get error string. Call this if WaitForFrame() returns 0 to determine + /// what the error is. + std::string GetError() const { + m_status = 0; + return GetSinkError(m_handle, &m_status); + } + + /// Enable or disable getting new frames. + /// Disabling will cause processFrame (for callback-based CvSinks) to not + /// be called and WaitForFrame() to not return. This can be used to save + /// processor resources when frames are not needed. + void SetEnabled(bool enabled) { + m_status = 0; + SetSinkEnabled(m_handle, enabled, &m_status); + } }; -int AddSourceListener( - std::function, int)> - callback, - int eventMask); +class SourceListener { + public: + enum Event { + kCreated = CS_SOURCE_CREATED, + kDestroyed = CS_SOURCE_DESTROYED, + kConnected = CS_SOURCE_CONNECTED, + kDisconnected = CS_SOURCE_DISCONNECTED + }; -void RemoveSourceListener(int handle); + SourceListener( + std::function + callback, + int eventMask) { + CS_Status status = 0; + m_handle = AddSourceListener( + [=](llvm::StringRef name, CS_Source sourceHandle, int event) { + callback(name, VideoSource{sourceHandle}, event); + }, + eventMask, &status); + } -int AddSinkListener( - std::function, int)> - callback, - int eventMask); + SourceListener(const SourceListener&) = delete; + SourceListener& operator=(const SourceListener&) = delete; -void RemoveSinkListener(int handle); + SourceListener(SourceListener&& rhs) noexcept : m_handle(rhs.m_handle) { + rhs.m_handle = 0; + } + + ~SourceListener() { + CS_Status status = 0; + if (m_handle != 0) RemoveSourceListener(m_handle, &status); + } + + private: + CS_Listener m_handle; +}; + +class SinkListener { + public: + enum Event { + kCreated = CS_SINK_CREATED, + kDestroyed = CS_SINK_DESTROYED, + kEnabled = CS_SINK_ENABLED, + kDisabled = CS_SINK_DISABLED + }; + + SinkListener( + std::function + callback, + int eventMask) { + CS_Status status = 0; + m_handle = AddSinkListener( + [=](llvm::StringRef name, CS_Sink sinkHandle, int event) { + callback(name, VideoSink{sinkHandle}, event); + }, + eventMask, &status); + } + + SinkListener(const SinkListener&) = delete; + SinkListener& operator=(const SinkListener&) = delete; + + SinkListener(SinkListener&& rhs) noexcept : m_handle(rhs.m_handle) { + rhs.m_handle = 0; + } + + ~SinkListener() { + CS_Status status = 0; + if (m_handle != 0) RemoveSinkListener(m_handle, &status); + } + + private: + CS_Listener m_handle; +}; } // namespace cs diff --git a/java/src/edu/wpi/cameraserver/CameraServerJNI.java b/java/src/edu/wpi/cameraserver/CameraServerJNI.java index 0a3ae50f33..b5845e5cb3 100644 --- a/java/src/edu/wpi/cameraserver/CameraServerJNI.java +++ b/java/src/edu/wpi/cameraserver/CameraServerJNI.java @@ -30,11 +30,11 @@ public class CameraServerJNI { if (is != null) { // create temporary file if (System.getProperty("os.name").startsWith("Windows")) - jniLibrary = File.createTempFile("NetworkTablesJNI", ".dll"); + jniLibrary = File.createTempFile("CameraServerJNI", ".dll"); else if (System.getProperty("os.name").startsWith("Mac")) - jniLibrary = File.createTempFile("libNetworkTablesJNI", ".dylib"); + jniLibrary = File.createTempFile("libCameraServerJNI", ".dylib"); else - jniLibrary = File.createTempFile("libNetworkTablesJNI", ".so"); + jniLibrary = File.createTempFile("libCameraServerJNI", ".so"); // flag for delete on exit jniLibrary.deleteOnExit(); OutputStream os = new FileOutputStream(jniLibrary); @@ -65,4 +65,113 @@ public class CameraServerJNI { } } + // + // Property Functions + // + public static native int getPropertyType(int property); + public static native boolean getBooleanProperty(int property); + public static native void setBooleanProperty(int property, boolean value); + public static native double getDoubleProperty(int property); + public static native void setDoubleProperty(int property, double value); + public static native double getDoublePropertyMin(int property); + public static native double getDoublePropertyMax(int property); + public static native String getStringProperty(int property); + public static native void setStringProperty(int property, String value); + public static native int getEnumProperty(int property); + public static native void setEnumProperty(int property, int value); + public static native String[] getEnumPropertyChoices(int property); + + // + // Source/Sink Functions + // + public static native int getSourceProperty(int handle, String name); + + // + // Source Creation Functions + // + public static native int createUSBSourceDev(String name, int dev); + public static native int createUSBSourcePath(String name, String path); + public static native int createHTTPSource(String name, String url); + public static native int createCvSource(String name, int numChannels); + + // + // Source Functions + // + public static native String getSourceName(int source); + public static native String getSourceDescription(int source); + public static native long getSourceLastFrameTime(int source); + public static native int getSourceNumChannels(int source); + public static native boolean isSourceConnected(int source); + public static native int copySource(int source); + public static native void releaseSource(int source); + + // + // OpenCV Source Functions + // + //public static native void putSourceImage(int source, int channel, CvMat image); + public static native void notifySourceFrame(int source); + //public static native void putSourceFrame(int source, CvMat image); + public static native void notifySourceError(int source, String msg); + public static native void setSourceConnected(int source, boolean connected); + public static native int createSourceProperty(int source, String name, int type); + //public static native int createSourcePropertyCallback(int source, String name, + // int type, + // void (*onChange)(String name, + // int property)); + public static native void removeSourceProperty(int source, String name); + + // + // Sink Creation Functions + // + public static native int createHTTPSink(String name, String listenAddress, int port); + public static native int createCvSink(String name); + //public static native int createCvSinkCallback(String name, + // void (*processFrame)(long time)); + + // + // Sink Functions + // + public static native String getSinkName(int sink); + public static native String getSinkDescription(int sink); + public static native void setSinkSource(int sink, int source); + public static native int getSinkSource(int sink); + public static native int copySink(int sink); + public static native void releaseSink(int sink); + + // + // Server Sink (e.g. HTTP) Functions + // + public static native void setSinkSourceChannel(int sink, int channel); + + // + // OpenCV Sink Functions + // + public static native long sinkWaitForFrame(int sink); + //public static native int getSinkImage(int sink, CvMat image); + //public static native int grabSinkFrame(int sink, CvMat image); + public static native String getSinkError(int sink); + public static native void setSinkEnabled(int sink, boolean enabled); + + // + // Listener Functions + // + //public static native int AddSourceListener(void (*callback)(String name, int source, + // int event), + // int eventMask); + + public static native void RemoveSourceListener(int handle); + + //public static native int AddSinkListener(void (*callback)(String name, int sink, int event), + // int eventMask); + + public static native void RemoveSinkListener(int handle); + + // + // Utility Functions + // + public static native USBCameraInfo[] enumerateUSBCameras(); + + public static native int[] enumerateSources(); + + public static native int[] enumerateSinks(); } diff --git a/java/src/edu/wpi/cameraserver/CvSink.java b/java/src/edu/wpi/cameraserver/CvSink.java new file mode 100644 index 0000000000..60b5897280 --- /dev/null +++ b/java/src/edu/wpi/cameraserver/CvSink.java @@ -0,0 +1,61 @@ +package edu.wpi.cameraserver; + +/// A sink for user code to accept video frames as OpenCV images. +public class CvSink extends VideoSink { + /// Create a sink for accepting OpenCV images. + /// WaitForFrame() must be called on the created sink to get each new + /// image. + /// @param name Source name (arbitrary unique identifier) + public CvSink(String name) { + super(CameraServerJNI.createCvSink(name)); + } + + /// Create a sink for accepting OpenCV images in a separate thread. + /// A thread will be created that calls WaitForFrame() and calls the + /// processFrame() callback each time a new frame arrives. + /// @param name Source name (arbitrary unique identifier) + /// @param processFrame Frame processing function; will be called with a + /// time=0 if an error occurred. processFrame should call GetImage() + /// or GetError() as needed, but should not call (except in very + /// unusual circumstances) WaitForImage(). + //public CvSink(llvm::StringRef name, + // std::function processFrame) { + // super(CameraServerJNI.createCvSinkCallback(name, processFrame)); + //} + + /// Wait for the next frame. This is a blocking call. + /// @return Frame time, or 0 on error (call GetError() to obtain the error + /// message). + public long waitForFrame() { + return CameraServerJNI.sinkWaitForFrame(m_handle); + } + + /// Get an OpenCV image from the specified channel. + /// @return False if image could not be obtained for some reason (e.g. + /// channel out of range) + //public boolean getImage(int channel, CvMat image) { + // return CameraServerJNI.getSinkImage(m_handle, channel, image); + //} + + /// Wait for the next frame and get the image from channel 0. Equivalent + /// to calling WaitForFrame() followed by GetImage(0, image). + /// @return Frame time, or 0 on error (call GetError() to obtain the error + /// message); + //public long frameGrab(CvMat image) { + // return CameraServerJNI.grabSinkFrame(m_handle, image); + //} + + /// Get error string. Call this if WaitForFrame() returns 0 to determine + /// what the error is. + public String getError() { + return CameraServerJNI.getSinkError(m_handle); + } + + /// Enable or disable getting new frames. + /// Disabling will cause processFrame (for callback-based CvSinks) to not + /// be called and WaitForFrame() to not return. This can be used to save + /// processor resources when frames are not needed. + public void setEnabled(boolean enabled) { + CameraServerJNI.setSinkEnabled(m_handle, enabled); + } +} diff --git a/java/src/edu/wpi/cameraserver/CvSource.java b/java/src/edu/wpi/cameraserver/CvSource.java new file mode 100644 index 0000000000..ae73c68ac0 --- /dev/null +++ b/java/src/edu/wpi/cameraserver/CvSource.java @@ -0,0 +1,68 @@ +package edu.wpi.cameraserver; + +/// A source that represents a video camera. +public class CvSource extends VideoSource { + /// Create an OpenCV source with a single channel. + /// @param name Source name (arbitrary unique identifier) + public CvSource(String name) { + super(CameraServerJNI.createCvSource(name, 1)); + } + + /// Create an OpenCV source. + /// @param name Source name (arbitrary unique identifier) + /// @param numChannels Number of channels + public CvSource(String name, int numChannels) { + super(CameraServerJNI.createCvSource(name, numChannels)); + } + + /// Put an OpenCV image onto the specified channel. + /// @param channel Channel number (range 0 to nChannels-1) + /// @param image OpenCV image + //public void putImage(int channel, Mat image); + + /// Signal sinks connected to this source that all new channel images have + /// been put to the stream and are ready to be read. + public void notifyFrame() { + CameraServerJNI.notifySourceFrame(m_handle); + } + + /// Put an OpenCV image onto channel 0 and notify sinks. + /// This is identical in behavior to calling PutImage(0, image) followed by + /// NotifyFrame(). + /// @param image OpenCV image + //public void putFrame(Mat image); + + /// Signal sinks that an error has occurred. This should be called instead + /// of NotifyFrame when an error occurs. + public void notifyError(String msg) { + CameraServerJNI.notifySourceError(m_handle, msg); + } + + /// Set source connection status. Defaults to true. + /// @param connected True for connected, false for disconnected + public void setConnected(boolean connected) { + CameraServerJNI.setSourceConnected(m_handle, connected); + } + + /// Create a property. + /// @param name Property name + /// @param type Property type + /// @return Property + //public VideoProperty createProperty(String name, VideoProperty.Type type); + + /// Create a property with a change callback. + /// @param name Property name + /// @param type Property type + /// @param onChange Callback to call when the property value changes + /// @return Property + //public VideoProperty createProperty( + // String name, VideoProperty.Type type, + // std::function + // onChange); + + /// Remove a property. + /// @param name Property name + public void removeProperty(String name) { + CameraServerJNI.removeSourceProperty(m_handle, name); + } +} diff --git a/java/src/edu/wpi/cameraserver/HTTPCamera.java b/java/src/edu/wpi/cameraserver/HTTPCamera.java new file mode 100644 index 0000000000..d61fb0aa1b --- /dev/null +++ b/java/src/edu/wpi/cameraserver/HTTPCamera.java @@ -0,0 +1,11 @@ +package edu.wpi.cameraserver; + +/// A source that represents a MJPEG-over-HTTP (IP) camera. +public class HTTPCamera extends VideoSource { + /// Create a source for a MJPEG-over-HTTP (IP) camera. + /// @param name Source name (arbitrary unique identifier) + /// @param url Camera URL (e.g. "http://10.x.y.11/video/stream.mjpg") + public HTTPCamera(String name, String url) { + super(CameraServerJNI.createHTTPSource(name, url)); + } +} diff --git a/java/src/edu/wpi/cameraserver/HTTPSink.java b/java/src/edu/wpi/cameraserver/HTTPSink.java new file mode 100644 index 0000000000..18d69f2cd3 --- /dev/null +++ b/java/src/edu/wpi/cameraserver/HTTPSink.java @@ -0,0 +1,27 @@ +package edu.wpi.cameraserver; + +/// A sink that acts as a MJPEG-over-HTTP network server. +public class HTTPSink extends VideoSink { + /// Create a MJPEG-over-HTTP server sink. + /// @param name Sink name (arbitrary unique identifier) + /// @param listenAddress TCP listen address (empty string for all addresses) + /// @param port TCP port number + public HTTPSink(String name, String listenAddress, int port) { + super(CameraServerJNI.createHTTPSink(name, listenAddress, port)); + } + + /// Create a MJPEG-over-HTTP server sink. + /// @param name Sink name (arbitrary unique identifier) + /// @param port TCP port number + public HTTPSink(String name, int port) { + this(name, "", port); + } + + /// Set what video channel should be served. + /// MJPEG-HTTP can only serve a single channel of video. + /// By default, channel 0 is served. + /// @param channel video channel to serve to clients + public void setSourceChannel(int channel) { + CameraServerJNI.setSinkSourceChannel(m_handle, channel); + } +} diff --git a/java/src/edu/wpi/cameraserver/USBCamera.java b/java/src/edu/wpi/cameraserver/USBCamera.java new file mode 100644 index 0000000000..14ad75de55 --- /dev/null +++ b/java/src/edu/wpi/cameraserver/USBCamera.java @@ -0,0 +1,24 @@ +package edu.wpi.cameraserver; + +/// A source that represents a USB camera. +public class USBCamera extends VideoSource { + /// Create a source for a USB camera based on device number. + /// @param name Source name (arbitrary unique identifier) + /// @param dev Device number (e.g. 0 for /dev/video0) + public USBCamera(String name, int dev) { + super(CameraServerJNI.createUSBSourceDev(name, dev)); + } + + /// Create a source for a USB camera based on device path. + /// @param name Source name (arbitrary unique identifier) + /// @param path Path to device (e.g. "/dev/video0" on Linux) + public USBCamera(String name, String path) { + super(CameraServerJNI.createUSBSourcePath(name, path)); + } + + /// Enumerate USB cameras on the local system. + /// @return Vector of USB camera information (one for each camera) + public static USBCameraInfo[] enumerateUSBCameras() { + return CameraServerJNI.enumerateUSBCameras(); + } +} diff --git a/java/src/edu/wpi/cameraserver/USBCameraInfo.java b/java/src/edu/wpi/cameraserver/USBCameraInfo.java new file mode 100644 index 0000000000..ce602d9ae2 --- /dev/null +++ b/java/src/edu/wpi/cameraserver/USBCameraInfo.java @@ -0,0 +1,21 @@ +package edu.wpi.cameraserver; + +/// USB camera information +public class USBCameraInfo { + public USBCameraInfo(int dev, String path, String name, int channels) { + this.dev = dev; + this.path = path; + this.name = name; + this.channels = channels; + } + + /// Device number (e.g. N in '/dev/videoN' on Linux) + public int dev; + /// Path to device if available (e.g. '/dev/video0' on Linux) + public String path; + /// Vendor/model name of the camera as provided by the USB driver + public String name; + /// Number of channels the camera provides (usually 1, but some cameras such + /// as stereo or depth cameras may provide multiple channels). + public int channels; +} diff --git a/java/src/edu/wpi/cameraserver/VideoSink.java b/java/src/edu/wpi/cameraserver/VideoSink.java new file mode 100644 index 0000000000..a645252f28 --- /dev/null +++ b/java/src/edu/wpi/cameraserver/VideoSink.java @@ -0,0 +1,68 @@ +package edu.wpi.cameraserver; + +/// A source for video that provides a sequence of frames. Each frame may +/// consist of multiple images (e.g. from a stereo or depth camera); these +/// are called channels. +public class VideoSink { + protected VideoSink(int handle) { + m_handle = handle; + } + + public synchronized void free() { + if (m_handle != 0) { + CameraServerJNI.releaseSink(m_handle); + } + m_handle = 0; + } + + public boolean isValid() { + return m_handle != 0; + } + + /// Get the name of the sink. The name is an arbitrary identifier + /// provided when the sink is created, and should be unique. + public String getName() { + return CameraServerJNI.getSinkName(m_handle); + } + + /// Get the sink description. This is sink-type specific. + public String getDescription() { + return CameraServerJNI.getSinkDescription(m_handle); + } + + /// Configure which source should provide frames to this sink. Each sink + /// can accept frames from only a single source, but a single source can + /// provide frames to multiple clients. + /// @param source Source + public void setSource(VideoSource source) { + CameraServerJNI.setSinkSource(m_handle, source.m_handle); + } + + /// Get the connected source. + /// @return Connected source; nullptr if no source connected. + public VideoSource getSource() { + // While VideoSource.free() will call releaseSource(), getSinkSource() + // increments the internal reference count so this is okay to do. + return new VideoSource(CameraServerJNI.getSinkSource(m_handle)); + } + + /// Get a property of the associated source. + /// @param name Property name + /// @return Property (type Property::kNone if no property with + /// the given name exists or no source connected) + //public VideoProperty getSourceProperty(String name) { + //} + + /// Enumerate all existing sinks. + /// @return Vector of sinks. + public static VideoSink[] enumerateSinks() { + int[] handles = CameraServerJNI.enumerateSinks(); + VideoSink[] rv = new VideoSink[handles.length]; + for (int i=0; i + +#include "llvm/SmallString.h" + +#include "cameraserver_cpp.h" + +extern "C" { + +CS_PropertyType CS_GetPropertyType(CS_Property property, CS_Status* status) { + return cs::GetPropertyType(property, status); +} + +CS_Bool CS_GetBooleanProperty(CS_Property property, CS_Status* status) { + return cs::GetBooleanProperty(property, status); +} + +void CS_SetBooleanProperty(CS_Property property, CS_Bool value, + CS_Status* status) { + return cs::SetBooleanProperty(property, value, status); +} + +double CS_GetDoubleProperty(CS_Property property, CS_Status* status) { + return cs::GetDoubleProperty(property, status); +} + +void CS_SetDoubleProperty(CS_Property property, double value, + CS_Status* status) { + return cs::SetDoubleProperty(property, value, status); +} + +double CS_GetDoublePropertyMin(CS_Property property, CS_Status* status) { + return cs::GetDoublePropertyMin(property, status); +} + +double CS_GetDoublePropertyMax(CS_Property property, CS_Status* status) { + return cs::GetDoublePropertyMax(property, status); +} + +char* CS_GetStringProperty(CS_Property property, CS_Status* status) { + return nullptr; // TODO +} + +void CS_SetStringProperty(CS_Property property, const char* value, + CS_Status* status) { + return cs::SetStringProperty(property, value, status); +} + +int CS_GetEnumProperty(CS_Property property, CS_Status* status) { + return cs::GetEnumProperty(property, status); +} + +void CS_SetEnumProperty(CS_Property property, int value, CS_Status* status) { + return cs::SetEnumProperty(property, value, status); +} + +char** CS_GetEnumPropertyChoices(CS_Property property, int* count, + CS_Status* status) { + return nullptr; // TODO +} + +CS_Property CS_GetSourceProperty(CS_Source handle, const char* name, + CS_Status* status) { + return cs::GetSourceProperty(handle, name, status); +} + +CS_Source CS_CreateUSBSourceDev(const char* name, int dev, CS_Status* status) { + return cs::CreateUSBSourceDev(name, dev, status); +} + +CS_Source CS_CreateUSBSourcePath(const char* name, const char* path, + CS_Status* status) { + return cs::CreateUSBSourcePath(name, path, status); +} + +CS_Source CS_CreateHTTPSource(const char* name, const char* url, + CS_Status* status) { + return cs::CreateHTTPSource(name, url, status); +} + +CS_Source CS_CreateCvSource(const char* name, int numChannels, + CS_Status* status) { + return cs::CreateCvSource(name, numChannels, status); +} + +char* CS_GetSourceName(CS_Source source, CS_Status* status) { + return nullptr; // TODO +} + +char* CS_GetSourceDescription(CS_Source source, CS_Status* status) { + return nullptr; // TODO +} + +uint64_t CS_GetSourceLastFrameTime(CS_Source source, CS_Status* status) { + return cs::GetSourceLastFrameTime(source, status); +} + +int CS_GetSourceNumChannels(CS_Source source, CS_Status* status) { + return cs::GetSourceNumChannels(source, status); +} + +CS_Bool CS_IsSourceConnected(CS_Source source, CS_Status* status) { + return cs::IsSourceConnected(source, status); +} + +CS_Source CS_CopySource(CS_Source source, CS_Status* status) { + return cs::CopySource(source, status); +} + +void CS_ReleaseSource(CS_Source source, CS_Status* status) { + return cs::ReleaseSource(source, status); +} + +void CS_PutSourceImage(CS_Source source, int channel, struct CvMat* image, + CS_Status* status) { + //TODO: return cs::PutSourceImage(source, channel, image, status); +} + +void CS_NotifySourceFrame(CS_Source source, CS_Status* status) { + return cs::NotifySourceFrame(source, status); +} + +void CS_PutSourceFrame(CS_Source source, struct CvMat* image, + CS_Status* status) { + //TODO: return cs::PutSourceFrame(source, image, status); +} + +void CS_NotifySourceError(CS_Source source, const char* msg, + CS_Status* status) { + return cs::NotifySourceError(source, msg, status); +} + +void CS_SetSourceConnected(CS_Source source, CS_Bool connected, + CS_Status* status) { + return cs::SetSourceConnected(source, connected, status); +} + +CS_Property CS_CreateSourceProperty(CS_Source source, const char* name, + enum CS_PropertyType type, + CS_Status* status) { + return cs::CreateSourceProperty(source, name, type, status); +} + +CS_Property CS_CreateSourcePropertyCallback( + CS_Source source, const char* name, enum CS_PropertyType type, void* data, + void (*onChange)(void* data, const char* name, CS_Property property), + CS_Status* status) { + return cs::CreateSourcePropertyCallback( + source, name, type, + [=](llvm::StringRef name, CS_Property property) { + // avoid the copy if possible + if (name[name.size()] == '\0') { + onChange(data, name.data(), property); + } else { + llvm::SmallString<128> copy{name}; + onChange(data, copy.c_str(), property); + } + }, + status); +} + +void CS_RemoveSourceProperty(CS_Source source, const char* name, + CS_Status* status) { + return cs::RemoveSourceProperty(source, name, status); +} + +CS_Sink CS_CreateHTTPSink(const char* name, const char* listenAddress, int port, + CS_Status* status) { + return cs::CreateHTTPSink(name, listenAddress, port, status); +} + +CS_Sink CS_CreateCvSink(const char* name, CS_Status* status) { + return cs::CreateCvSink(name, status); +} + +CS_Sink CS_CreateCvSinkCallback(const char* name, void* data, + void (*processFrame)(void* data, uint64_t time), + CS_Status* status) { + return cs::CreateCvSinkCallback( + name, [=](uint64_t time) { processFrame(data, time); }, status); +} + +char* CS_GetSinkName(CS_Sink sink, CS_Status* status) { + return nullptr; // TODO +} + +char* CS_GetSinkDescription(CS_Sink sink, CS_Status* status) { + return nullptr; // TODO +} + +void CS_SetSinkSource(CS_Sink sink, CS_Source source, CS_Status* status) { + return cs::SetSinkSource(sink, source, status); +} + +CS_Source CS_GetSinkSource(CS_Sink sink, CS_Status* status) { + return cs::GetSinkSource(sink, status); +} + +CS_Sink CS_CopySink(CS_Sink sink, CS_Status* status) { + return cs::CopySink(sink, status); +} + +void CS_ReleaseSink(CS_Sink sink, CS_Status* status) { + return cs::ReleaseSink(sink, status); +} + +void CS_SetSinkSourceChannel(CS_Sink sink, int channel, CS_Status* status) { + return cs::SetSinkSourceChannel(sink, channel, status); +} + +uint64_t CS_SinkWaitForFrame(CS_Sink sink, CS_Status* status) { + return cs::SinkWaitForFrame(sink, status); +} + +CS_Bool CS_GetSinkImage(CS_Sink sink, int channel, struct CvMat* image, + CS_Status* status) { + return 0; //TODO: cs::GetSinkImage(sink, channel, image, status); +} + +uint64_t CS_GrabSinkFrame(CS_Sink sink, struct CvMat* image, + CS_Status* status) { + return 0; // TODO: cs::GrabSinkFrame(sink, image, status); +} + +char* CS_GetSinkError(CS_Sink sink, CS_Status* status) { + return nullptr; // TODO +} + +void CS_SetSinkEnabled(CS_Sink sink, CS_Bool enabled, CS_Status* status) { + return cs::SetSinkEnabled(sink, enabled, status); +} + +CS_Listener CS_AddSourceListener(void* data, + void (*callback)(void* data, const char* name, + CS_Source source, int event), + int eventMask, CS_Status* status) { + return cs::AddSourceListener( + [=](llvm::StringRef name, CS_Source source, int event) { + // avoid the copy if possible + if (name[name.size()] == '\0') { + callback(data, name.data(), source, event); + } else { + llvm::SmallString<128> copy{name}; + callback(data, copy.c_str(), source, event); + } + }, + eventMask, status); +} + +void CS_RemoveSourceListener(CS_Listener handle, CS_Status* status) { + return cs::RemoveSourceListener(handle, status); +} + +CS_Listener CS_AddSinkListener(void* data, + void (*callback)(void* data, const char* name, + CS_Sink sink, int event), + int eventMask, CS_Status* status) { + return cs::AddSinkListener( + [=](llvm::StringRef name, CS_Sink sink, int event) { + // avoid the copy if possible + if (name[name.size()] == '\0') { + callback(data, name.data(), sink, event); + } else { + llvm::SmallString<128> copy{name}; + callback(data, copy.c_str(), sink, event); + } + }, + eventMask, status); +} + +void CS_RemoveSinkListener(CS_Listener handle, CS_Status* status) { + return cs::RemoveSinkListener(handle, status); +} + +CS_USBCameraInfo* CS_EnumerateUSBCameras(int* count, CS_Status* status) { + return nullptr; // TODO +} + +void CS_FreeEnumeratedUSBCameras(CS_USBCameraInfo* cameras) { + // TODO +} + +CS_Source* CS_EnumerateSources(int* count, CS_Status* status) { + return nullptr; // TODO +} + +void CS_FreeEnumeratedSources(CS_Source* sources) { + // TODO +} + +CS_Sink* CS_EnumerateSinks(int* count, CS_Status* status) { + return nullptr; // TODO +} + +void CS_FreeEnumeratedSinks(CS_Sink* sinks) { + // TODO +} + +void CS_FreeString(char* str) { + // TODO +} + +void CS_FreeEnumPropertyChoices(char** choices) { + // TODO +} + +} // extern "C" diff --git a/src/cameraserver_cpp.cpp b/src/cameraserver_cpp.cpp index cea36f01c2..54cd34c19a 100644 --- a/src/cameraserver_cpp.cpp +++ b/src/cameraserver_cpp.cpp @@ -11,213 +11,337 @@ using namespace cs; -bool Property::GetBoolean() const { +namespace cs { + +// +// Property Functions +// + +CS_PropertyType GetPropertyType(CS_Property property, CS_Status* status) { + return CS_PROP_NONE; // TODO +} + +bool GetBooleanProperty(CS_Property property, CS_Status* status) { return false; // TODO } -void Property::SetBoolean(bool value) { +void SetBooleanProperty(CS_Property property, bool value, CS_Status* status) { // TODO } -double Property::GetDouble() const { - return 0; // TODO +double GetDoubleProperty(CS_Property property, CS_Status* status) { + return 0.0; // TODO } -void Property::SetDouble(double value) { +void SetDoubleProperty(CS_Property property, double value, CS_Status* status) { // TODO } -double Property::GetMin() const { - return std::numeric_limits::min(); // TODO +double GetDoublePropertyMin(CS_Property property, CS_Status* status) { + return 0.0; // TODO } -double Property::GetMax() const { - return std::numeric_limits::max(); // TODO +double GetDoublePropertyMax(CS_Property property, CS_Status* status) { + return 0.0; // TODO } -std::string Property::GetString() const { +std::string GetStringProperty(CS_Property property, CS_Status* status) { return ""; // TODO } -void Property::SetString(llvm::StringRef value) { +void GetStringProperty(CS_Property property, llvm::SmallVectorImpl& value, + CS_Status* status) { // TODO } -int Property::GetEnum() const { +void SetStringProperty(CS_Property property, llvm::StringRef value, + CS_Status* status) { + // TODO +} + +int GetEnumProperty(CS_Property property, CS_Status* status) { return 0; // TODO } -void Property::SetEnum(int value) { +void SetEnumProperty(CS_Property property, int value, CS_Status* status) { // TODO } -std::vector Property::GetChoices() const { - return std::vector(); // TODO +std::vector GetEnumPropertyChoices(CS_Property property, + CS_Status* status) { + return std::vector{}; // TODO } -VideoSource::~VideoSource() { - // TODO +// +// Source/Sink Functions +// + +CS_Property GetSourceProperty(CS_Source handle, llvm::StringRef name, + CS_Status* status) { + return 0; // TODO } -std::string VideoSource::GetDescription() const { +// +// Source Creation Functions +// + +CS_Source CreateUSBSourceDev(llvm::StringRef name, int dev, CS_Status* status) { + return 0; // TODO +} + +CS_Source CreateUSBSourcePath(llvm::StringRef name, llvm::StringRef path, + CS_Status* status) { + return 0; // TODO +} + +CS_Source CreateHTTPSource(llvm::StringRef name, llvm::StringRef url, + CS_Status* status) { + return 0; // TODO +} + +CS_Source CreateCvSource(llvm::StringRef name, int numChannels, + CS_Status* status) { + return 0; // TODO +} + +// +// Source Functions +// + +std::string GetSourceName(CS_Source source, CS_Status* status) { return ""; // TODO } -uint64_t VideoSource::GetLastFrameTime() const { +void GetSourceName(CS_Source sink, llvm::SmallVectorImpl& name, + CS_Status* status) { + // TODO +} + +std::string GetSourceDescription(CS_Source source, CS_Status* status) { + return ""; // TODO +} + +void GetSourceDescription(CS_Source source, llvm::SmallVectorImpl& desc, + CS_Status* status) { + // TODO +} + +uint64_t GetSourceLastFrameTime(CS_Source source, CS_Status* status) { return 0; // TODO } -int VideoSource::GetNumChannels() const { +int GetSourceNumChannels(CS_Source source, CS_Status* status) { return 0; // TODO } -bool VideoSource::IsConnected() const { +bool IsSourceConnected(CS_Source source, CS_Status* status) { return false; // TODO } -Property VideoSource::GetProperty(llvm::StringRef name) { - return Property(); // TODO +CS_Source CopySource(CS_Source source, CS_Status* status) { + return source; // TODO } -std::vector> VideoSource::EnumerateSources() { - return std::vector>(); // TODO -} - -std::shared_ptr CameraSource::CreateUSB(llvm::StringRef name, - int dev) { - return nullptr; // TODO -} - -std::shared_ptr CameraSource::CreateUSB(llvm::StringRef name, - llvm::StringRef path) { - return nullptr; // TODO -} - -std::shared_ptr CameraSource::CreateHTTP(llvm::StringRef name, - llvm::StringRef url) { - return nullptr; // TODO -} - -std::vector CameraSource::EnumerateUSBCameras() { - return std::vector(); // TODO -} - -void CvSource::PutImage(int channel, cv::Mat* image) { +void ReleaseSource(CS_Source source, CS_Status* status) { // TODO } -void CvSource::NotifyFrame() { +// +// OpenCV Source Functions +// + +void PutSourceImage(CS_Source source, int channel, cv::Mat* image, + CS_Status* status) { // TODO } -void CvSource::PutFrame(cv::Mat* image) { - PutImage(0, image); - NotifyFrame(); -} - -void CvSource::NotifyError(llvm::StringRef msg) { +void NotifySourceFrame(CS_Source source, CS_Status* status) { // TODO } -void CvSource::SetConnected(bool connected) { +void PutSourceFrame(CS_Source source, cv::Mat* image, CS_Status* status) { // TODO } -Property CvSource::CreateProperty(llvm::StringRef name, Property::Type type) { - return Property(); // TODO -} - -Property CvSource::CreateProperty( - llvm::StringRef name, Property::Type type, - std::function onChange) { - return Property(); // TODO -} - -void CvSource::RemoveProperty(llvm::StringRef name) { +void NotifySourceError(CS_Source source, llvm::StringRef msg, + CS_Status* status) { // TODO } -std::shared_ptr CvSource::Create(llvm::StringRef name, - int nChannels) { - return nullptr; // TODO -} - -VideoSink::~VideoSink() { +void SetSourceConnected(CS_Source source, bool connected, CS_Status* status) { // TODO } -std::string VideoSink::GetDescription() const { - return ""; // TODO -} - -void VideoSink::SetSource(std::shared_ptr source) { - // TODO -} - -Property VideoSink::GetSourceProperty(llvm::StringRef name) { - return Property(); // TODO -} - -std::vector> VideoSink::EnumerateSinks() { - return std::vector>(); // TODO -} - -void ServerSink::SetSourceChannel(int channel) { - // TODO -} - -std::shared_ptr ServerSink::CreateHTTP( - llvm::StringRef name, llvm::StringRef listenAddress, int port) { - return nullptr; // TODO -} - -uint64_t CvSink::WaitForFrame() const { +CS_Property CreateSourceProperty(CS_Source source, llvm::StringRef name, + CS_PropertyType type, CS_Status* status) { return 0; // TODO } -bool CvSink::GetImage(int channel, cv::Mat* image) const { - return false; // TODO -} - -uint64_t CvSink::FrameGrab(cv::Mat* image) const { +CS_Property CreateSourcePropertyCallback( + CS_Source source, llvm::StringRef name, CS_PropertyType type, + std::function onChange, + CS_Status* status) { return 0; // TODO } -std::string CvSink::GetError() const { +void RemoveSourceProperty(CS_Source source, llvm::StringRef name, + CS_Status* status) { + // TODO +} + +// +// Sink Creation Functions +// + +CS_Sink CreateHTTPSink(llvm::StringRef name, llvm::StringRef listenAddress, + int port, CS_Status* status) { + return 0; // TODO +} + +CS_Sink CreateCvSink(llvm::StringRef name, CS_Status* status) { + return 0; // TODO +} + +CS_Sink CreateCvSinkCallback(llvm::StringRef name, + std::function processFrame, + CS_Status* status) { + return 0; // TODO +} + +// +// Sink Functions +// +std::string GetSinkName(CS_Sink sink, CS_Status* status) { return ""; // TODO } -void CvSink::SetEnabled(bool enabled) { +void GetSinkName(CS_Sink sink, llvm::SmallVectorImpl& name, + CS_Status* status) { // TODO } -std::shared_ptr CvSink::Create(llvm::StringRef name) { - return nullptr; // TODO +std::string GetSinkDescription(CS_Sink sink, CS_Status* status) { + return ""; // TODO } -std::shared_ptr CvSink::Create( - llvm::StringRef name, std::function processFrame) { - return nullptr; // TODO +void GetSinkDescription(CS_Sink sink, llvm::SmallVectorImpl& desc, + CS_Status* status) { + // TODO } -int cs::AddSourceListener( - std::function, int)> +void SetSinkSource(CS_Sink sink, CS_Source source, CS_Status* status) { + // TODO +} + +CS_Source GetSinkSource(CS_Sink sink, CS_Status* status) { + return 0; // TODO +} + +CS_Sink CopySink(CS_Sink sink, CS_Status* status) { + return sink; // TODO +} + +void ReleaseSink(CS_Sink sink, CS_Status* status) { + // TODO +} + +// +// Server Sink (e.g. HTTP) Functions +// + +void SetSinkSourceChannel(CS_Sink sink, int channel, CS_Status* status) { + // TODO +} + +// +// OpenCV Sink Functions +// + +uint64_t SinkWaitForFrame(CS_Sink sink, CS_Status* status) { + return 0; // TODO +} + +bool GetSinkImage(CS_Sink sink, int channel, cv::Mat* image, + CS_Status* status) { + return false; // TODO +} + +uint64_t GrabSinkFrame(CS_Sink sink, cv::Mat* image, CS_Status* status) { + return 0; // TODO +} + +std::string GetSinkError(CS_Sink sink, CS_Status* status) { + return ""; // TODO +} + +void GetSinkError(CS_Sink sink, llvm::SmallVectorImpl msg, + CS_Status* status) { + // TODO +} + +void SetSinkEnabled(CS_Sink sink, bool enabled, CS_Status* status) { + // TODO +} + +// +// Listener Functions +// + +CS_Listener AddSourceListener( + std::function callback, - int eventMask) { + int eventMask, CS_Status* status) { return 0; // TODO } -void cs::RemoveSourceListener(int handle) { +void RemoveSourceListener(CS_Listener handle, CS_Status* status) { // TODO } -int cs::AddSinkListener( - std::function, int)> - callback, - int eventMask) { +CS_Listener AddSinkListener( + std::function callback, + int eventMask, CS_Status* status) { return 0; // TODO } -void cs::RemoveSinkListener(int handle) { +void RemoveSinkListener(CS_Listener handle, CS_Status* status) { // TODO } + +// +// Utility Functions +// + +void EnumerateSourceHandles(llvm::SmallVectorImpl& handles, + CS_Status* status) { + // TODO +} + +void EnumerateSinkHandles(llvm::SmallVectorImpl& handles, + CS_Status* status) { + // TODO +} + +std::vector VideoSource::EnumerateSources() { + std::vector sources; + llvm::SmallVector handles; + CS_Status status = 0; + EnumerateSourceHandles(handles, &status); + sources.reserve(handles.size()); + for (int handle : handles) + sources.emplace_back(VideoSource{handle}); + return sources; +} + +std::vector VideoSink::EnumerateSinks() { + std::vector sinks; + llvm::SmallVector handles; + CS_Status status = 0; + EnumerateSinkHandles(handles, &status); + sinks.reserve(handles.size()); + for (int handle : handles) + sinks.emplace_back(VideoSink{handle}); + return sinks; +} + +} // namespace cs