diff --git a/src/USBCameraImpl.cpp b/src/USBCameraImpl.cpp new file mode 100644 index 0000000000..9aec2c0b83 --- /dev/null +++ b/src/USBCameraImpl.cpp @@ -0,0 +1,719 @@ +/*----------------------------------------------------------------------------*/ +/* 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 "USBCameraImpl.h" + +#include + +#ifdef __linux__ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#elif defined(_WIN32) + +#endif + +#include "llvm/raw_ostream.h" +#include "llvm/SmallString.h" + +#include "cameraserver_cpp.h" +#include "c_util.h" +#include "Handle.h" +#include "Log.h" + +using namespace cs; + +#ifdef __linux__ + +static inline int ControlIdToProperty(__u32 control_id) { + return (control_id & 0xffff) + 1; +} + +#ifdef VIDIOC_QUERY_EXT_CTRL +USBCameraImpl::PropertyData::PropertyData( + const struct v4l2_query_ext_ctrl& ctrl) + : id(ctrl.id & V4L2_CTRL_ID_MASK), + type(ctrl.type), + minimum(ctrl.minimum), + maximum(ctrl.maximum), + step(ctrl.step), + defaultValue(ctrl.default_value) { + // propType + switch (ctrl.type) { + case V4L2_CTRL_TYPE_INTEGER: + case V4L2_CTRL_TYPE_INTEGER64: + propType = CS_PROP_DOUBLE; + break; + case V4L2_CTRL_TYPE_BOOLEAN: + propType = CS_PROP_BOOLEAN; + break; + case V4L2_CTRL_TYPE_INTEGER_MENU: + case V4L2_CTRL_TYPE_MENU: + propType = CS_PROP_ENUM; + break; + case V4L2_CTRL_TYPE_STRING: + propType = CS_PROP_STRING; + break; + default: + return; // others unsupported + } + + // name + std::size_t len = 0; + while (len < sizeof(ctrl.name) && ctrl.name[len] != '\0') ++len; + name.assign(ctrl.name, len); +} +#endif + +USBCameraImpl::PropertyData::PropertyData(const struct v4l2_queryctrl& ctrl) + : id(ctrl.id & V4L2_CTRL_ID_MASK), + type(ctrl.type), + minimum(ctrl.minimum), + maximum(ctrl.maximum), + step(ctrl.step), + defaultValue(ctrl.default_value) { + // propType + switch (ctrl.type) { + case V4L2_CTRL_TYPE_INTEGER: + case V4L2_CTRL_TYPE_INTEGER64: + propType = CS_PROP_DOUBLE; + break; + case V4L2_CTRL_TYPE_BOOLEAN: + propType = CS_PROP_BOOLEAN; + break; + case V4L2_CTRL_TYPE_INTEGER_MENU: + case V4L2_CTRL_TYPE_MENU: + propType = CS_PROP_ENUM; + break; + case V4L2_CTRL_TYPE_STRING: + propType = CS_PROP_STRING; + break; + default: + return; // others unsupported + } + + // name + std::size_t len = 0; + while (len < sizeof(ctrl.name) && ctrl.name[len] != '\0') ++len; + name.assign(reinterpret_cast(ctrl.name), len); +} + +static inline int CheckedIoctl(int fd, unsigned long req, void* data, + const char* name, bool quiet) { + int retval = ioctl(fd, req, data); + if (!quiet && retval < 0) + ERROR("ioctl " << name << "failed: " << std::strerror(errno)); + return retval; +} + +#define DoIoctl(fd, req, data) CheckedIoctl(fd, req, data, #req, false) +#define TryIoctl(fd, req, data) CheckedIoctl(fd, req, data, #req, true) + +static int ExtCtrlIoctl(int fd, __u32* id, USBCameraImpl::PropertyData* prop) { + int rc; +#ifdef VIDIOC_QUERY_EXT_CTRL + v4l2_query_ext_ctrl qc_ext; + std::memset(&qc_ext, 0, sizeof(qc_ext)); + qc_ext.id = *id; + rc = TryIoctl(fd, VIDIOC_QUERY_EXT_CTRL, &qc_ext); + if (rc == 0) { + *id = qc_ext.id; // copy back + // We don't support array types + if (qc_ext.elems > 1 || qc_ext.nr_of_dims > 0) return 0; + *prop = qc_ext; + return 0; + } +#endif + // Fall back to normal QUERYCTRL + struct v4l2_queryctrl qc; + std::memset(&qc, 0, sizeof(qc)); + qc.id = *id; + rc = TryIoctl(fd, VIDIOC_QUERYCTRL, &qc); + *id = qc.id; // copy back + if (rc != 0) return rc; + *prop = qc; + return 0; +} + +static int GetIntCtrlIoctl(int fd, unsigned id, int type, int64_t* value) { + unsigned ctrl_class = V4L2_CTRL_ID2CLASS(id); + if (type == V4L2_CTRL_TYPE_INTEGER64 || V4L2_CTRL_DRIVER_PRIV(id) || + (ctrl_class != V4L2_CTRL_CLASS_USER && + ctrl_class != V4L2_CID_PRIVATE_BASE)) { + // Use extended control + struct v4l2_ext_control ctrl; + struct v4l2_ext_controls ctrls; + std::memset(&ctrl, 0, sizeof(ctrl)); + ctrl.id = id; + ctrls.ctrl_class = ctrl_class; + ctrls.count = 1; + ctrls.controls = &ctrl; + int rc = DoIoctl(fd, VIDIOC_G_EXT_CTRLS, &ctrls); + if (rc < 0) return rc; + *value = ctrl.value; + } else { + // Use normal control + struct v4l2_control ctrl; + ctrl.id = id; + int rc = DoIoctl(fd, VIDIOC_G_CTRL, &ctrl); + if (rc < 0) return rc; + *value = ctrl.value; + } + return 0; +} + +static int SetIntCtrlIoctl(int fd, unsigned id, int type, int64_t value) { + unsigned ctrl_class = V4L2_CTRL_ID2CLASS(id); + if (type == V4L2_CTRL_TYPE_INTEGER64 || V4L2_CTRL_DRIVER_PRIV(id) || + (ctrl_class != V4L2_CTRL_CLASS_USER && + ctrl_class != V4L2_CID_PRIVATE_BASE)) { + // Use extended control + struct v4l2_ext_control ctrl; + struct v4l2_ext_controls ctrls; + std::memset(&ctrl, 0, sizeof(ctrl)); + ctrl.id = id; + if (type == V4L2_CTRL_TYPE_INTEGER64) + ctrl.value64 = value; + else + ctrl.value = static_cast<__s32>(value); + ctrls.ctrl_class = ctrl_class; + ctrls.count = 1; + ctrls.controls = &ctrl; + return DoIoctl(fd, VIDIOC_S_EXT_CTRLS, &ctrls); + } else { + // Use normal control + struct v4l2_control ctrl; + ctrl.id = id; + ctrl.value = static_cast<__s32>(value); + return DoIoctl(fd, VIDIOC_S_CTRL, &ctrl); + } +} + +static std::string GetDescriptionImpl(const char* cpath) { + llvm::StringRef path{cpath}; + int fd; + char pathBuf[128]; + + // If trying to get by id or path, follow symlink + if (path.startswith("/dev/v4l/by-id/")) { + ssize_t n = readlink(cpath, pathBuf, sizeof(pathBuf)); + if (n > 0) path = llvm::StringRef(pathBuf, n); + } else if (path.startswith("/dev/v4l/by-path/")) { + ssize_t n = readlink(cpath, pathBuf, sizeof(pathBuf)); + if (n > 0) path = llvm::StringRef(pathBuf, n); + } + + if (path.startswith("/dev/video")) { + // Sometimes the /sys tree gives a better name. + llvm::SmallString<64> ifpath{"/sys/class/video4linux/"}; + ifpath += path.substr(5); + ifpath += "/device/interface"; + fd = open(ifpath.c_str(), O_RDONLY); + if (fd >= 0) { + char readBuf[128]; + ssize_t n = read(fd, readBuf, sizeof(readBuf)); + if (n > 0) { + close(fd); + return llvm::StringRef(readBuf, n).rtrim(); + } + close(fd); + } + } + + // Otherwise use an ioctl to query the caps and get the card name + fd = open(cpath, O_RDWR); + if (fd >= 0) { + struct v4l2_capability vcap; + if (DoIoctl(fd, VIDIOC_QUERYCAP, &vcap) >= 0) { + close(fd); + llvm::StringRef card{reinterpret_cast(vcap.card)}; + // try to convert "UVC Camera (0000:0000)" into a better name + int vendor = 0; + int product = 0; + if (card.startswith("UVC Camera (") && + !card.substr(12, 4).getAsInteger(16, vendor) && + !card.substr(17, 4).getAsInteger(16, product)) { + switch (vendor) { + case 0x046d: + switch (product) { + case 0x081b: return "Logitech, Inc. Webcam C310"; + case 0x0825: return "Logitech, Inc. Webcam C270"; + } + break; + } + } + return card; + } + close(fd); + } + + return std::string{}; +} + +USBCameraImpl::USBCameraImpl(llvm::StringRef name, llvm::StringRef path) + : SourceImpl{name}, + m_path{path}, + m_description{GetDescriptionImpl(m_path.c_str())}, + m_fd{open(m_path.c_str(), O_RDWR)}, + m_active{false} { + if (m_fd >= 0) m_connected = true; +} + +USBCameraImpl::~USBCameraImpl() { Stop(); } + +llvm::StringRef USBCameraImpl::GetDescription( + llvm::SmallVectorImpl& buf) const { + return m_description; +} + +void USBCameraImpl::CacheProperty(PropertyData&& prop) const { + // NOTE: must be called with m_mutex held! + int propIndex = ControlIdToProperty(prop.id); + m_properties[prop.name] = propIndex; + m_property_data[propIndex] = std::move(prop); +} + +void USBCameraImpl::CacheProperties() const { + int fd = m_fd.load(); + if (fd < 0) return; + + std::lock_guard lock(m_mutex); + if (m_properties_cached) return; // double-checked locking + + constexpr __u32 nextFlags = V4L2_CTRL_FLAG_NEXT_CTRL +#ifdef V4L2_CTRL_FLAG_NEXT_COMPOUND + | V4L2_CTRL_FLAG_NEXT_COMPOUND +#endif + ; + __u32 id = nextFlags; + PropertyData prop; + + while (ExtCtrlIoctl(fd, &id, &prop) == 0) { + CacheProperty(std::move(prop)); + id |= nextFlags; + } + + if (id == nextFlags) { + // try just enumerating standard... + for (id = V4L2_CID_BASE; id < V4L2_CID_LASTP1; ++id) { + if (ExtCtrlIoctl(fd, &id, &prop) == 0) CacheProperty(std::move(prop)); + } + // ... and custom controls + for (id = V4L2_CID_PRIVATE_BASE; ExtCtrlIoctl(fd, &id, &prop) == 0; ++id) { + CacheProperty(std::move(prop)); + } + } + + m_properties_cached = true; +} + +int USBCameraImpl::GetProperty(llvm::StringRef name) const { + if (!m_properties_cached) CacheProperties(); + std::lock_guard lock(m_mutex); + return m_properties.lookup(name); +} + +llvm::ArrayRef USBCameraImpl::EnumerateProperties( + llvm::SmallVectorImpl& vec) const { + if (!m_properties_cached) CacheProperties(); + std::lock_guard lock(m_mutex); + for (const auto& data : m_property_data) vec.push_back(data.getFirst()); + return vec; +} + +bool USBCameraImpl::GetPropertyTypeValueFd(int property, + CS_PropertyType propType, + unsigned* id, int* type, + int* fd) const { + // Get id and type from cached properties + { + if (!m_properties_cached) CacheProperties(); + std::lock_guard lock(m_mutex); + auto it = m_property_data.find(property); + if (it == m_property_data.end()) { + //*status = ; + return false; + } + if (it->getSecond().propType != propType) { + //*status = ; + return false; + } + *id = it->getSecond().id; + *type = it->getSecond().type; + } + + *fd = m_fd.load(); + if (*fd < 0) { + //*status = ; + return false; + } + return true; +} + +CS_PropertyType USBCameraImpl::GetPropertyType(int property) const { + if (!m_properties_cached) CacheProperties(); + std::lock_guard lock(m_mutex); + auto it = m_property_data.find(property); + if (it == m_property_data.end()) { + //*status = ; + return CS_PROP_NONE; + } + return it->getSecond().propType; +} + +llvm::StringRef USBCameraImpl::GetPropertyName( + int property, llvm::SmallVectorImpl& buf) const { + if (!m_properties_cached) CacheProperties(); + std::lock_guard lock(m_mutex); + auto it = m_property_data.find(property); + if (it == m_property_data.end()) { + //*status = ; + return llvm::StringRef{}; + } + return it->getSecond().name; // safe because we never modify it after caching +} + +bool USBCameraImpl::GetBooleanProperty(int property) const { + unsigned id; + int type; + int fd; + if (!GetPropertyTypeValueFd(property, CS_PROP_BOOLEAN, &id, &type, &fd)) + return false; + + int64_t value = 0; + if (GetIntCtrlIoctl(fd, id, type, &value) < 0) { + //*status = ; + return false; + } + return value != 0; +} + +void USBCameraImpl::SetBooleanProperty(int property, bool value) { + unsigned id; + int type; + int fd; + if (!GetPropertyTypeValueFd(property, CS_PROP_BOOLEAN, &id, &type, &fd)) + return; + + if (SetIntCtrlIoctl(fd, id, type, value ? 1 : 0) < 0) { + //*status = ; + } +} + +double USBCameraImpl::GetDoubleProperty(int property) const { + unsigned id; + int type; + int fd; + if (!GetPropertyTypeValueFd(property, CS_PROP_DOUBLE, &id, &type, &fd)) + return false; + + int64_t value = 0; + if (GetIntCtrlIoctl(fd, id, type, &value) < 0) { + //*status = ; + return false; + } + return value; +} + +void USBCameraImpl::SetDoubleProperty(int property, double value) { + unsigned id; + int type; + int fd; + if (!GetPropertyTypeValueFd(property, CS_PROP_DOUBLE, &id, &type, &fd)) + return; + + if (SetIntCtrlIoctl(fd, id, type, static_cast(value)) < 0) { + //*status = ; + } +} + +double USBCameraImpl::GetPropertyMin(int property) const { + if (!m_properties_cached) CacheProperties(); + std::lock_guard lock(m_mutex); + auto it = m_property_data.find(property); + if (it == m_property_data.end()) { + //*status = ; + return 0; + } + return it->getSecond().minimum; +} + +double USBCameraImpl::GetPropertyMax(int property) const { + if (!m_properties_cached) CacheProperties(); + std::lock_guard lock(m_mutex); + auto it = m_property_data.find(property); + if (it == m_property_data.end()) { + //*status = ; + return 0; + } + return it->getSecond().maximum; +} + +double USBCameraImpl::GetPropertyStep(int property) const { + if (!m_properties_cached) CacheProperties(); + std::lock_guard lock(m_mutex); + auto it = m_property_data.find(property); + if (it == m_property_data.end()) { + //*status = ; + return 0; + } + return it->getSecond().step; +} + +double USBCameraImpl::GetPropertyDefault(int property) const { + if (!m_properties_cached) CacheProperties(); + std::lock_guard lock(m_mutex); + auto it = m_property_data.find(property); + if (it == m_property_data.end()) { + //*status = ; + return 0; + } + return it->getSecond().defaultValue; +} + +llvm::StringRef USBCameraImpl::GetStringProperty( + int property, llvm::SmallVectorImpl& buf) const { + unsigned id; + int type; + int fd; + if (!GetPropertyTypeValueFd(property, CS_PROP_STRING, &id, &type, &fd)) + return llvm::StringRef{}; + + struct v4l2_ext_control ctrl; + struct v4l2_ext_controls ctrls; + std::memset(&ctrl, 0, sizeof(ctrl)); + ctrl.id = id; + ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(id); + ctrls.count = 1; + ctrls.controls = &ctrl; + int rc = DoIoctl(fd, VIDIOC_G_EXT_CTRLS, &ctrls); + if (rc < 0) { + //*status = ; + return llvm::StringRef{}; + } + buf.append(ctrl.string, ctrl.string + std::strlen(ctrl.string)); + return llvm::StringRef(buf.data(), buf.size()); +} + +void USBCameraImpl::SetStringProperty(int property, llvm::StringRef value) { + unsigned id; + std::size_t maximum; + // Get id and maximum from cached properties + { + if (!m_properties_cached) CacheProperties(); + std::lock_guard lock(m_mutex); + auto it = m_property_data.find(property); + if (it == m_property_data.end()) { + //*status = ; + return; + } + if (it->getSecond().propType != CS_PROP_STRING) { + //*status = ; + return; + } + id = it->getSecond().id; + maximum = static_cast(it->getSecond().maximum); + } + + int fd = m_fd.load(); + if (fd < 0) { + //*status = ; + return; + } + + llvm::SmallString<64> str{value.substr(0, std::min(value.size(), maximum))}; + + struct v4l2_ext_control ctrl; + struct v4l2_ext_controls ctrls; + std::memset(&ctrl, 0, sizeof(ctrl)); + ctrl.id = id; + ctrl.size = str.size(); + ctrl.string = const_cast(str.c_str()); + ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(id); + ctrls.count = 1; + ctrls.controls = &ctrl; + int rc = DoIoctl(fd, VIDIOC_S_EXT_CTRLS, &ctrls); + if (rc < 0) { + //*status = ; + return; + } +} + +int USBCameraImpl::GetEnumProperty(int property) const { + unsigned id; + int type; + int fd; + if (!GetPropertyTypeValueFd(property, CS_PROP_ENUM, &id, &type, &fd)) + return false; + + int64_t value = 0; + if (GetIntCtrlIoctl(fd, id, type, &value) < 0) { + //*status = ; + return false; + } + return static_cast(value); +} + +void USBCameraImpl::SetEnumProperty(int property, int value) { + unsigned id; + int type; + int fd; + if (!GetPropertyTypeValueFd(property, CS_PROP_ENUM, &id, &type, &fd)) return; + + if (SetIntCtrlIoctl(fd, id, type, value) < 0) { + //*status = ; + } +} + +std::vector USBCameraImpl::GetEnumPropertyChoices( + int property) const { + unsigned id; + unsigned minimum; + unsigned maximum; + // Get id, minimum, and maximum from cached properties + { + if (!m_properties_cached) CacheProperties(); + std::lock_guard lock(m_mutex); + auto it = m_property_data.find(property); + if (it == m_property_data.end()) { + //*status = ; + return std::vector{}; + } + if (it->getSecond().propType != CS_PROP_ENUM) { + //*status = ; + return std::vector{}; + } + id = it->getSecond().id; + minimum = static_cast(it->getSecond().minimum); + maximum = static_cast(it->getSecond().maximum); + } + + int fd = m_fd.load(); + if (fd < 0) { + //*status = ; + return std::vector{}; + } + + std::vector vec(maximum + 1); + v4l2_querymenu qmenu; + std::memset(&qmenu, 0, sizeof(qmenu)); + qmenu.id = id; + for (unsigned i = minimum; i <= maximum; ++i) { + qmenu.index = static_cast<__u32>(i); + if (TryIoctl(fd, VIDIOC_QUERYMENU, &qmenu) != 0) continue; + vec[i] = reinterpret_cast(qmenu.name); + } + + return vec; +} + +void USBCameraImpl::Stop() { + m_active = false; + + // close camera connection + int fd = m_fd.exchange(-1); + if (fd >= 0) close(fd); + + // join camera thread +} + +namespace cs { + +CS_Source CreateUSBSourceDev(llvm::StringRef name, int dev, CS_Status* status) { + llvm::SmallString<32> path; + llvm::raw_svector_ostream oss{path}; + oss << "/dev/video" << dev; + return CreateUSBSourcePath(name, oss.str(), status); +} + +CS_Source CreateUSBSourcePath(llvm::StringRef name, llvm::StringRef path, + CS_Status* status) { + auto source = std::make_shared(name, path); + return Sources::GetInstance().Allocate(SourceData::kUSB, source); +} + +std::vector EnumerateUSBCameras(CS_Status* status) { + std::vector retval; + + if (DIR* dp = opendir("/dev")) { + while (struct dirent* ep = readdir(dp)) { + llvm::StringRef fname{ep->d_name}; + if (!fname.startswith("video")) continue; + + USBCameraInfo info; + info.dev = -1; + fname.substr(5).getAsInteger(10, info.dev); + llvm::SmallString<32> path{"/dev/"}; + path += fname; + info.path = path.str(); + + info.name = GetDescriptionImpl(path.c_str()); + if (info.name.empty()) continue; + + retval.emplace_back(std::move(info)); + } + closedir(dp); + } else { + //*status = ; + ERROR("Could not open /dev"); + return retval; + } + + // sort by device number + std::sort(retval.begin(), retval.end(), + [](const USBCameraInfo& a, const USBCameraInfo& b) { + return a.dev < b.dev; + }); + + return retval; +} + +} // namespace cs + +extern "C" { + +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_USBCameraInfo* CS_EnumerateUSBCameras(int* count, CS_Status* status) { + auto cameras = cs::EnumerateUSBCameras(status); + CS_USBCameraInfo* out = static_cast( + std::malloc(cameras.size() * sizeof(CS_USBCameraInfo))); + *count = cameras.size(); + for (std::size_t i = 0; i < cameras.size(); ++i) { + out[i].dev = cameras[i].dev; + out[i].path = ConvertToC(cameras[i].path); + out[i].name = ConvertToC(cameras[i].name); + } + return out; +} + +void CS_FreeEnumeratedUSBCameras(CS_USBCameraInfo* cameras, int count) { + if (!cameras) return; + for (int i = 0; i < count; ++i) { + std::free(cameras[i].path); + std::free(cameras[i].name); + } + std::free(cameras); +} + +} // extern "C" + +#endif // __linux__ diff --git a/src/USBCameraImpl.h b/src/USBCameraImpl.h new file mode 100644 index 0000000000..088efdb50e --- /dev/null +++ b/src/USBCameraImpl.h @@ -0,0 +1,104 @@ +/*----------------------------------------------------------------------------*/ +/* 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_USBCAMERAIMPL_H_ +#define CAMERASERVER_USBCAMERAIMPL_H_ + +#include +#include + +#ifdef __linux__ +#include +#endif + +#include "llvm/raw_ostream.h" +#include "llvm/DenseMap.h" +#include "llvm/SmallVector.h" +#include "llvm/StringMap.h" +#include "support/raw_istream.h" + +#include "SourceImpl.h" + +namespace cs { + +class USBCameraImpl : public SourceImpl { + public: + USBCameraImpl(llvm::StringRef name, llvm::StringRef path); + ~USBCameraImpl() override; + + llvm::StringRef GetDescription( + llvm::SmallVectorImpl& buf) const override; + + // Property functions + int GetProperty(llvm::StringRef name) const override; + llvm::ArrayRef EnumerateProperties( + llvm::SmallVectorImpl& vec) const override; + CS_PropertyType GetPropertyType(int property) const override; + llvm::StringRef GetPropertyName( + int property, llvm::SmallVectorImpl& buf) const override; + bool GetBooleanProperty(int property) const override; + void SetBooleanProperty(int property, bool value) override; + double GetDoubleProperty(int property) const override; + void SetDoubleProperty(int property, double value) override; + double GetPropertyMin(int property) const override; + double GetPropertyMax(int property) const override; + double GetPropertyStep(int property) const override; + double GetPropertyDefault(int property) const override; + llvm::StringRef GetStringProperty( + int property, llvm::SmallVectorImpl& buf) const override; + void SetStringProperty(int property, llvm::StringRef value) override; + int GetEnumProperty(int property) const override; + void SetEnumProperty(int property, int value) override; + std::vector GetEnumPropertyChoices(int property) const override; + + void Stop(); + + struct PropertyData { + PropertyData() = default; +#ifdef __linux__ +#ifdef VIDIOC_QUERY_EXT_CTRL + PropertyData(const struct v4l2_query_ext_ctrl& ctrl); +#endif + PropertyData(const struct v4l2_queryctrl& ctrl); +#endif + + std::string name; + unsigned id; // implementation-level id + int type; // implementation type, not CS_PropertyType! + CS_PropertyType propType; + double minimum; + double maximum; + double step; + double defaultValue; + }; + + private: + mutable llvm::DenseMap m_property_data; + mutable llvm::StringMap m_properties; + mutable std::atomic_bool m_properties_cached{false}; + + void CacheProperty(PropertyData&& prop) const; + void CacheProperties() const; + bool GetPropertyTypeValueFd(int property, CS_PropertyType propType, + unsigned* id, int* type, int* fd) const; + + void CameraThreadMain(); + + std::string m_path; + std::string m_description; + + std::atomic_int m_fd; + + std::atomic_bool m_active; // set to false to terminate threads + std::thread m_cameraThread; + + mutable std::mutex m_mutex; +}; + +} // namespace cs + +#endif // CAMERASERVER_USBCAMERAIMPL_H_ diff --git a/src/c_util.h b/src/c_util.h new file mode 100644 index 0000000000..4aa1df916d --- /dev/null +++ b/src/c_util.h @@ -0,0 +1,22 @@ +/*----------------------------------------------------------------------------*/ +/* 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_C_UTIL_H_ +#define CAMERASERVER_C_UTIL_H_ + +namespace cs { + +inline char* ConvertToC(llvm::StringRef in) { + char* out = static_cast(std::malloc(in.size() + 1)); + std::memmove(out, in.data(), in.size()); + out[in.size()] = '\0'; + return out; +} + +} // namespace cs + +#endif // CAMERASERVER_C_UTIL_H_ diff --git a/src/cameraserver_c.cpp b/src/cameraserver_c.cpp index b4b53fb4e9..4c6e793415 100644 --- a/src/cameraserver_c.cpp +++ b/src/cameraserver_c.cpp @@ -13,17 +13,7 @@ #include "llvm/SmallString.h" #include "cameraserver_cpp.h" - -// -// Conversion helpers -// - -static char* ConvertToC(llvm::StringRef in) { - char* out = static_cast(std::malloc(in.size() + 1)); - std::memmove(out, in.data(), in.size()); - out[in.size()] = '\0'; - return out; -} +#include "c_util.h" extern "C" { @@ -35,7 +25,7 @@ char* CS_GetPropertyName(CS_Property property, CS_Status* status) { llvm::SmallString<128> buf; auto str = cs::GetPropertyName(property, buf, status); if (*status != 0) return nullptr; - return ConvertToC(str); + return cs::ConvertToC(str); } CS_Bool CS_GetBooleanProperty(CS_Property property, CS_Status* status) { @@ -76,7 +66,7 @@ char* CS_GetStringProperty(CS_Property property, CS_Status* status) { llvm::SmallString<128> buf; auto str = cs::GetStringProperty(property, buf, status); if (*status != 0) return nullptr; - return ConvertToC(str); + return cs::ConvertToC(str); } void CS_SetStringProperty(CS_Property property, const char* value, @@ -98,19 +88,10 @@ char** CS_GetEnumPropertyChoices(CS_Property property, int* count, char** out = static_cast(std::malloc(choices.size() * sizeof(char*))); *count = choices.size(); for (std::size_t i = 0; i < choices.size(); ++i) - out[i] = ConvertToC(choices[i]); + out[i] = cs::ConvertToC(choices[i]); return out; } -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); @@ -124,14 +105,14 @@ char* CS_GetSourceName(CS_Source source, CS_Status* status) { llvm::SmallString<128> buf; auto str = cs::GetSourceName(source, buf, status); if (*status != 0) return nullptr; - return ConvertToC(str); + return cs::ConvertToC(str); } char* CS_GetSourceDescription(CS_Source source, CS_Status* status) { llvm::SmallString<128> buf; auto str = cs::GetSourceDescription(source, buf, status); if (*status != 0) return nullptr; - return ConvertToC(str); + return cs::ConvertToC(str); } uint64_t CS_GetSourceLastFrameTime(CS_Source source, CS_Status* status) { @@ -221,14 +202,14 @@ char* CS_GetSinkName(CS_Sink sink, CS_Status* status) { llvm::SmallString<128> buf; auto str = cs::GetSinkName(sink, buf, status); if (*status != 0) return nullptr; - return ConvertToC(str); + return cs::ConvertToC(str); } char* CS_GetSinkDescription(CS_Sink sink, CS_Status* status) { llvm::SmallString<128> buf; auto str = cs::GetSinkDescription(sink, buf, status); if (*status != 0) return nullptr; - return ConvertToC(str); + return cs::ConvertToC(str); } void CS_SetSinkSource(CS_Sink sink, CS_Source source, CS_Status* status) { @@ -261,7 +242,7 @@ char* CS_GetSinkError(CS_Sink sink, CS_Status* status) { llvm::SmallString<128> buf; auto str = cs::GetSinkError(sink, buf, status); if (*status != 0) return nullptr; - return ConvertToC(str); + return cs::ConvertToC(str); } void CS_SetSinkEnabled(CS_Sink sink, CS_Bool enabled, CS_Status* status) { @@ -310,28 +291,6 @@ void CS_RemoveSinkListener(CS_Listener handle, CS_Status* status) { return cs::RemoveSinkListener(handle, status); } -CS_USBCameraInfo* CS_EnumerateUSBCameras(int* count, CS_Status* status) { - auto cameras = cs::EnumerateUSBCameras(status); - CS_USBCameraInfo* out = static_cast( - std::malloc(cameras.size() * sizeof(CS_USBCameraInfo))); - *count = cameras.size(); - for (std::size_t i = 0; i < cameras.size(); ++i) { - out[i].dev = cameras[i].dev; - out[i].path = ConvertToC(cameras[i].path); - out[i].name = ConvertToC(cameras[i].name); - } - return out; -} - -void CS_FreeEnumeratedUSBCameras(CS_USBCameraInfo* cameras, int count) { - if (!cameras) return; - for (int i = 0; i < count; ++i) { - std::free(cameras[i].path); - std::free(cameras[i].name); - } - std::free(cameras); -} - CS_Source* CS_EnumerateSources(int* count, CS_Status* status) { llvm::SmallVector buf; auto handles = cs::EnumerateSourceHandles(buf, status); diff --git a/src/cameraserver_cpp.cpp b/src/cameraserver_cpp.cpp index 85596d4e9d..450bf5b2dd 100644 --- a/src/cameraserver_cpp.cpp +++ b/src/cameraserver_cpp.cpp @@ -171,15 +171,6 @@ std::vector GetEnumPropertyChoices(CS_Property property, // 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 @@ -499,10 +490,6 @@ void RemoveSinkListener(CS_Listener handle, CS_Status* status) { // Utility Functions // -std::vector EnumerateUSBCameras(CS_Status* status) { - return std::vector{}; // TODO -} - llvm::ArrayRef EnumerateSourceHandles( llvm::SmallVectorImpl& vec, CS_Status* status) { return Sources::GetInstance().GetAll(vec);