diff --git a/src/CvSourceImpl.cpp b/src/CvSourceImpl.cpp new file mode 100644 index 0000000000..be55530efc --- /dev/null +++ b/src/CvSourceImpl.cpp @@ -0,0 +1,327 @@ +/*----------------------------------------------------------------------------*/ +/* 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 "CvSourceImpl.h" + +#include "llvm/STLExtras.h" +#include "opencv2/core/core.hpp" +#include "opencv2/highgui/highgui.hpp" +#include "support/timestamp.h" + +#include "cameraserver_cpp.h" +#include "c_util.h" +#include "Handle.h" +#include "Log.h" + +using namespace cs; + +CvSourceImpl::CvSourceImpl(llvm::StringRef name, const VideoMode& mode) + : SourceImpl{name} { + m_mode = mode; + m_videoModes.push_back(m_mode); + + // Create jpeg quality property + m_compressionParams.push_back(CV_IMWRITE_JPEG_QUALITY); + m_compressionParams.push_back(80); + + m_qualityProperty = + CreateProperty("jpeg_quality", CS_PROP_INTEGER, 0, 100, 1, 80, 80); +} + +CvSourceImpl::~CvSourceImpl() {} + +bool CvSourceImpl::IsConnected() const { return m_connected; } + +bool CvSourceImpl::CacheProperties(CS_Status* status) const { + // Doesn't need to do anything. + m_properties_cached = true; + return true; +} + +void CvSourceImpl::SetProperty(int property, int value, CS_Status* status) { + std::lock_guard lock(m_mutex); + auto prop = static_cast(GetProperty(property)); + if (!prop) { + *status = CS_INVALID_PROPERTY; + return; + } + if ((prop->propType & (CS_PROP_BOOLEAN | CS_PROP_INTEGER | CS_PROP_ENUM)) == + 0) { + *status = CS_WRONG_PROPERTY_TYPE; + return; + } + prop->value = value; +} + +void CvSourceImpl::SetStringProperty(int property, llvm::StringRef value, + CS_Status* status) { + std::lock_guard lock(m_mutex); + auto prop = static_cast(GetProperty(property)); + if (!prop) { + *status = CS_INVALID_PROPERTY; + return; + } + if (prop->propType != CS_PROP_STRING) { + *status = CS_WRONG_PROPERTY_TYPE; + return; + } + prop->valueStr = value; +} + +bool CvSourceImpl::SetVideoMode(const VideoMode& mode, CS_Status* status) { + // can't set video mode on OpenCV source + return false; +} + +void CvSourceImpl::NumSinksChanged() { + // ignore +} + +void CvSourceImpl::NumSinksEnabledChanged() { + // ignore +} + +void CvSourceImpl::PutFrame(cv::Mat& image) { + std::unique_lock lock(m_mutex); + if (auto prop = GetProperty(m_qualityProperty)) { + if (prop->value >= 0 && prop->value <= 100) + m_compressionParams[1] = prop->value; + } + cv::imencode(".jpg", image, m_jpegBuf, m_compressionParams); + SourceImpl::PutFrame( + VideoMode::kMJPEG, + llvm::StringRef(reinterpret_cast(m_jpegBuf.data()), + m_jpegBuf.size()), + wpi::Now()); +} + +void CvSourceImpl::NotifyError(llvm::StringRef msg) { + // TODO +} + +void CvSourceImpl::SetConnected(bool connected) { m_connected = connected; } + +CS_Property CvSourceImpl::CreateProperty(llvm::StringRef name, + CS_PropertyType type, int minimum, + int maximum, int step, + int defaultValue, int value) { + std::unique_lock lock(m_mutex); + int& ndx = m_properties[name]; + if (ndx == 0) { + // create a new index + ndx = m_propertyData.size() + 1; + m_propertyData.emplace_back(llvm::make_unique( + name, type, minimum, maximum, step, defaultValue, value)); + } else { + // update all but value + auto prop = GetProperty(ndx); + prop->propType = type; + prop->minimum = minimum; + prop->maximum = maximum; + prop->step = step; + prop->defaultValue = defaultValue; + } + return ndx; +} + +void CvSourceImpl::SetEnumPropertyChoices(CS_Property property, + llvm::ArrayRef choices, + CS_Status* status) { + std::lock_guard lock(m_mutex); + auto prop = GetProperty(property); + if (!prop) { + *status = CS_INVALID_PROPERTY; + return; + } + if (prop->propType != CS_PROP_ENUM) { + *status = CS_WRONG_PROPERTY_TYPE; + return; + } + prop->enumChoices = choices; +} + +void CvSourceImpl::RemoveProperty(CS_Property property) { + // TODO +} + +void CvSourceImpl::RemoveProperty(llvm::StringRef name) { + // TODO +} + +namespace cs { + +CS_Source CreateCvSource(llvm::StringRef name, const VideoMode& mode, + CS_Status* status) { + auto source = std::make_shared(name, mode); + return Sources::GetInstance().Allocate(SourceData::kCv, source); +} + +void PutSourceFrame(CS_Source source, cv::Mat& image, CS_Status* status) { + auto data = Sources::GetInstance().Get(source); + if (!data || data->type != SourceData::kCv) { + *status = CS_INVALID_HANDLE; + return; + } + static_cast(*data->source).PutFrame(image); +} + +void NotifySourceError(CS_Source source, llvm::StringRef msg, + CS_Status* status) { + auto data = Sources::GetInstance().Get(source); + if (!data || data->type != SourceData::kCv) { + *status = CS_INVALID_HANDLE; + return; + } + static_cast(*data->source).NotifyError(msg); +} + +void SetSourceConnected(CS_Source source, bool connected, CS_Status* status) { + auto data = Sources::GetInstance().Get(source); + if (!data || data->type != SourceData::kCv) { + *status = CS_INVALID_HANDLE; + return; + } + static_cast(*data->source).SetConnected(connected); +} + +void SetSourceDescription(CS_Source source, llvm::StringRef description, + CS_Status* status) { + auto data = Sources::GetInstance().Get(source); + if (!data || data->type != SourceData::kCv) { + *status = CS_INVALID_HANDLE; + return; + } + static_cast(*data->source).SetDescription(description); +} + +CS_Property CreateSourceProperty(CS_Source source, llvm::StringRef name, + CS_PropertyType type, int minimum, int maximum, + int step, int defaultValue, int value, + CS_Status* status) { + auto data = Sources::GetInstance().Get(source); + if (!data || data->type != SourceData::kCv) { + *status = CS_INVALID_HANDLE; + return -1; + } + return static_cast(*data->source) + .CreateProperty(name, type, minimum, maximum, step, defaultValue, value); +} + +CS_Property CreateSourcePropertyCallback( + CS_Source source, llvm::StringRef name, CS_PropertyType type, int minimum, + int maximum, int step, int defaultValue, int value, + std::function onChange, CS_Status* status) { + auto data = Sources::GetInstance().Get(source); + if (!data || data->type != SourceData::kCv) { + *status = CS_INVALID_HANDLE; + return -1; + } + return static_cast(*data->source) + .CreateProperty(name, type, minimum, maximum, step, defaultValue, value, + onChange); +} + +void SetSourceEnumPropertyChoices(CS_Source source, CS_Property property, + llvm::ArrayRef choices, + CS_Status* status) { + auto data = Sources::GetInstance().Get(source); + if (!data || data->type != SourceData::kCv) { + *status = CS_INVALID_HANDLE; + return; + } + static_cast(*data->source) + .SetEnumPropertyChoices(property, choices, status); +} + +void RemoveSourceProperty(CS_Source source, CS_Property property, + CS_Status* status) { + auto data = Sources::GetInstance().Get(source); + if (!data || data->type != SourceData::kCv) { + *status = CS_INVALID_HANDLE; + return; + } + static_cast(*data->source).RemoveProperty(property); +} + +void RemoveSourceProperty(CS_Source source, llvm::StringRef name, + CS_Status* status) { + auto data = Sources::GetInstance().Get(source); + if (!data || data->type != SourceData::kCv) { + *status = CS_INVALID_HANDLE; + return; + } + static_cast(*data->source).RemoveProperty(name); +} + +} // namespace cs + +extern "C" { + +CS_Source CS_CreateCvSource(const char* name, const CS_VideoMode* mode, + CS_Status* status) { + return cs::CreateCvSource(name, static_cast(*mode), + status); +} + +void CS_PutSourceFrame(CS_Source source, struct CvMat* image, + CS_Status* status) { + auto mat = cv::cvarrToMat(image); + return cs::PutSourceFrame(source, mat, 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); +} + +void CS_SetSourceDescription(CS_Source source, const char* description, + CS_Status* status) { + return cs::SetSourceDescription(source, description, status); +} + +CS_Property CS_CreateSourceProperty(CS_Source source, const char* name, + enum CS_PropertyType type, int minimum, + int maximum, int step, int defaultValue, + int value, CS_Status* status) { + return cs::CreateSourceProperty(source, name, type, minimum, maximum, step, + defaultValue, value, status); +} + +CS_Property CS_CreateSourcePropertyCallback( + CS_Source source, const char* name, enum CS_PropertyType type, int minimum, + int maximum, int step, int defaultValue, int value, void* data, + void (*onChange)(void* data, CS_Property property), CS_Status* status) { + return cs::CreateSourcePropertyCallback( + source, name, type, minimum, maximum, step, defaultValue, value, + [=](CS_Property property) { onChange(data, property); }, status); +} + +void CS_SetSourceEnumPropertyChoices(CS_Source source, CS_Property property, + const char** choices, int count, + CS_Status* status) { + llvm::SmallVector vec; + vec.reserve(count); + for (int i = 0; i < count; ++i) vec.push_back(choices[i]); + return cs::SetSourceEnumPropertyChoices(source, property, vec, status); +} + +void CS_RemoveSourceProperty(CS_Source source, CS_Property property, + CS_Status* status) { + return cs::RemoveSourceProperty(source, property, status); +} + +void CS_RemoveSourcePropertyByName(CS_Source source, const char* name, + CS_Status* status) { + return cs::RemoveSourceProperty(source, name, status); +} + +} // extern "C" diff --git a/src/CvSourceImpl.h b/src/CvSourceImpl.h new file mode 100644 index 0000000000..91d29db5db --- /dev/null +++ b/src/CvSourceImpl.h @@ -0,0 +1,80 @@ +/*----------------------------------------------------------------------------*/ +/* 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_CVSOURCEIMPL_H_ +#define CAMERASERVER_CVSOURCEIMPL_H_ + +#include +#include +#include + +#include "llvm/StringMap.h" + +#include "SourceImpl.h" + +namespace cs { + +class CvSourceImpl : public SourceImpl { + public: + CvSourceImpl(llvm::StringRef name, const VideoMode& mode); + ~CvSourceImpl() override; + + bool IsConnected() const override; + + // Property functions + void SetProperty(int property, int value, CS_Status* status) override; + void SetStringProperty(int property, llvm::StringRef value, + CS_Status* status) override; + + bool SetVideoMode(const VideoMode& mode, CS_Status* status) override; + + void NumSinksChanged() override; + void NumSinksEnabledChanged() override; + + // OpenCV-specific functions + void PutFrame(cv::Mat& image); + void NotifyError(llvm::StringRef msg); + void SetConnected(bool connected); + CS_Property CreateProperty(llvm::StringRef name, CS_PropertyType type, + int minimum, int maximum, int step, + int defaultValue, int value); + CS_Property CreateProperty( + llvm::StringRef name, CS_PropertyType type, int minimum, int maximum, + int step, int defaultValue, int value, + std::function onChange); + void SetEnumPropertyChoices(CS_Property property, + llvm::ArrayRef choices, + CS_Status* status); + void RemoveProperty(CS_Property property); + void RemoveProperty(llvm::StringRef name); + + // Property data + class PropertyData : public PropertyBase { + public: + PropertyData() = default; + PropertyData(llvm::StringRef name_, CS_PropertyType type_, int minimum_, + int maximum_, int step_, int defaultValue_, int value_) + : PropertyBase{name_, type_, minimum_, maximum_, + step_, defaultValue_, value_} {} + ~PropertyData() override = default; + + std::function onChange; + }; + + protected: + bool CacheProperties(CS_Status* status) const override; + + private: + std::atomic_bool m_connected{true}; + std::vector m_jpegBuf; + std::vector m_compressionParams; + int m_qualityProperty; +}; + +} // namespace cs + +#endif // CAMERASERVER_CVSOURCEIMPL_H_ diff --git a/src/cameraserver_c.cpp b/src/cameraserver_c.cpp index 56eae13602..e9b4d7cbac 100644 --- a/src/cameraserver_c.cpp +++ b/src/cameraserver_c.cpp @@ -80,12 +80,6 @@ CS_Source CS_CreateHTTPCamera(const char* name, const char* url, return cs::CreateHTTPCamera(name, url, status); } -CS_Source CS_CreateCvSource(const char* name, const CS_VideoMode* mode, - CS_Status* status) { - return cs::CreateCvSource(name, static_cast(*mode), - status); -} - char* CS_GetSourceName(CS_Source source, CS_Status* status) { llvm::SmallString<128> buf; auto str = cs::GetSourceName(source, buf, status); @@ -182,63 +176,6 @@ void CS_ReleaseSource(CS_Source source, CS_Status* status) { return cs::ReleaseSource(source, status); } -void CS_PutSourceFrame(CS_Source source, struct CvMat* image, - CS_Status* status) { - auto mat = cv::cvarrToMat(image); - return cs::PutSourceFrame(source, mat, 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); -} - -void CS_SetSourceDescription(CS_Source source, const char* description, - CS_Status* status) { - return cs::SetSourceDescription(source, description, status); -} - -CS_Property CS_CreateSourceProperty(CS_Source source, const char* name, - enum CS_PropertyType type, int minimum, - int maximum, int step, int defaultValue, - int value, CS_Status* status) { - return cs::CreateSourceProperty(source, name, type, minimum, maximum, step, - defaultValue, value, status); -} - -CS_Property CS_CreateSourcePropertyCallback( - CS_Source source, const char* name, enum CS_PropertyType type, int minimum, - int maximum, int step, int defaultValue, int value, void* data, - void (*onChange)(void* data, CS_Property property), CS_Status* status) { - return cs::CreateSourcePropertyCallback( - source, name, type, minimum, maximum, step, defaultValue, value, - [=](CS_Property property) { onChange(data, property); }, status); -} - -void CS_SetSourceEnumPropertyChoices(CS_Source source, CS_Property property, - const char** choices, int count, - CS_Status* status) { - llvm::SmallVector vec; - vec.reserve(count); - for (int i = 0; i < count; ++i) vec.push_back(choices[i]); - return cs::SetSourceEnumPropertyChoices(source, property, vec, status); -} - -void CS_RemoveSourceProperty(CS_Source source, CS_Property property, - CS_Status* status) { - return cs::RemoveSourceProperty(source, property, status); -} - -void CS_RemoveSourcePropertyByName(CS_Source source, const char* name, - CS_Status* status) { - return cs::RemoveSourceProperty(source, name, status); -} - CS_Sink CS_CreateCvSink(const char* name, CS_Status* status) { return cs::CreateCvSink(name, status); } diff --git a/src/cameraserver_cpp.cpp b/src/cameraserver_cpp.cpp index de989c9639..e3875423b1 100644 --- a/src/cameraserver_cpp.cpp +++ b/src/cameraserver_cpp.cpp @@ -147,11 +147,6 @@ CS_Source CreateHTTPCamera(llvm::StringRef name, llvm::StringRef url, return 0; // TODO } -CS_Source CreateCvSource(llvm::StringRef name, const VideoMode& mode, - CS_Status* status) { - return 0; // TODO -} - // // Source Functions // @@ -325,58 +320,6 @@ void ReleaseSource(CS_Source source, CS_Status* status) { if (--(data->refCount) == 0) inst.Free(source); } -// -// OpenCV Source Functions -// - -void PutSourceFrame(CS_Source source, cv::Mat& image, CS_Status* status) { - // TODO -} - -void NotifySourceError(CS_Source source, llvm::StringRef msg, - CS_Status* status) { - // TODO -} - -void SetSourceConnected(CS_Source source, bool connected, CS_Status* status) { - // TODO -} - -void SetSourceDescription(CS_Source source, llvm::StringRef description, - CS_Status* status) { - // TODO -} - -CS_Property CreateSourceProperty(CS_Source source, llvm::StringRef name, - CS_PropertyType type, int minimum, int maximum, - int step, int defaultValue, int value, - CS_Status* status) { - return 0; // TODO -} - -CS_Property CreateSourcePropertyCallback( - CS_Source source, llvm::StringRef name, CS_PropertyType type, int minimum, - int maximum, int step, int defaultValue, int value, - std::function onChange, CS_Status* status) { - return 0; // TODO -} - -void SetSourceEnumPropertyChoices(CS_Source source, CS_Property property, - llvm::ArrayRef choices, - CS_Status* status) { - // TODO -} - -void RemoveSourceProperty(CS_Source source, CS_Property property, - CS_Status* status) { - // TODO -} - -void RemoveSourceProperty(CS_Source source, llvm::StringRef name, - CS_Status* status) { - // TODO -} - // // Sink Creation Functions //