Refactor UsbCameraProperty.

This commit is contained in:
Peter Johnson
2016-12-22 22:08:08 -08:00
parent d6ef2c04a5
commit 5e38d8f28a
6 changed files with 406 additions and 343 deletions

View File

@@ -88,22 +88,6 @@ static __u32 FromPixelFormat(VideoMode::PixelFormat pixelFormat) {
}
}
// Removes non-alphanumeric characters and replaces spaces with underscores.
// e.g. "Zoom, Absolute" -> "zoom_absolute", "Pan (Absolute)" -> "pan_absolute"
static llvm::StringRef NormalizeName(llvm::StringRef name,
llvm::SmallVectorImpl<char>& buf) {
bool newWord = false;
for (auto ch : name) {
if (std::isalnum(ch)) {
if (newWord) buf.push_back('_');
newWord = false;
buf.push_back(std::tolower(ch));
} else if (!buf.empty())
newWord = true;
}
return llvm::StringRef(buf.data(), buf.size());
}
static bool IsPercentageProperty(llvm::StringRef name) {
if (name.startswith("raw_")) name = name.substr(4);
return name == "brightness" || name == "contrast" || name == "saturation" ||
@@ -111,117 +95,21 @@ static bool IsPercentageProperty(llvm::StringRef name) {
name == "exposure_absolute";
}
int UsbCameraImpl::RawToPercentage(const PropertyData& rawProp, int rawValue) {
int UsbCameraImpl::RawToPercentage(const UsbCameraProperty& rawProp,
int rawValue) {
return 100.0 * (rawValue - rawProp.minimum) /
(rawProp.maximum - rawProp.minimum);
}
int UsbCameraImpl::PercentageToRaw(const PropertyData& rawProp,
int UsbCameraImpl::PercentageToRaw(const UsbCameraProperty& rawProp,
int percentValue) {
return rawProp.minimum +
(rawProp.maximum - rawProp.minimum) * (percentValue / 100.0);
}
#ifdef VIDIOC_QUERY_EXT_CTRL
UsbCameraImpl::PropertyData::PropertyData(
const struct v4l2_query_ext_ctrl& ctrl)
: PropertyImpl(llvm::StringRef{}, CS_PROP_NONE, ctrl.step,
ctrl.default_value, 0),
id(ctrl.id & V4L2_CTRL_ID_MASK),
type(ctrl.type) {
hasMinimum = true;
minimum = ctrl.minimum;
hasMaximum = true;
maximum = ctrl.maximum;
// propKind
switch (ctrl.type) {
case V4L2_CTRL_TYPE_INTEGER:
case V4L2_CTRL_TYPE_INTEGER64:
propKind = CS_PROP_INTEGER;
break;
case V4L2_CTRL_TYPE_BOOLEAN:
propKind = CS_PROP_BOOLEAN;
break;
case V4L2_CTRL_TYPE_INTEGER_MENU:
case V4L2_CTRL_TYPE_MENU:
propKind = CS_PROP_ENUM;
break;
case V4L2_CTRL_TYPE_STRING:
propKind = CS_PROP_STRING;
break;
default:
return; // others unsupported
}
// name
std::size_t len = 0;
while (len < sizeof(ctrl.name) && ctrl.name[len] != '\0') ++len;
llvm::SmallString<64> name_buf;
name = NormalizeName(llvm::StringRef(ctrl.name, len), name_buf);
}
#endif
UsbCameraImpl::PropertyData::PropertyData(const struct v4l2_queryctrl& ctrl)
: PropertyImpl(llvm::StringRef{}, CS_PROP_NONE, ctrl.step,
ctrl.default_value, 0),
id(ctrl.id & V4L2_CTRL_ID_MASK),
type(ctrl.type) {
hasMinimum = true;
minimum = ctrl.minimum;
hasMaximum = true;
maximum = ctrl.maximum;
// propKind
switch (ctrl.type) {
case V4L2_CTRL_TYPE_INTEGER:
case V4L2_CTRL_TYPE_INTEGER64:
propKind = CS_PROP_INTEGER;
break;
case V4L2_CTRL_TYPE_BOOLEAN:
propKind = CS_PROP_BOOLEAN;
break;
case V4L2_CTRL_TYPE_INTEGER_MENU:
case V4L2_CTRL_TYPE_MENU:
propKind = CS_PROP_ENUM;
break;
case V4L2_CTRL_TYPE_STRING:
propKind = CS_PROP_STRING;
break;
default:
return; // others unsupported
}
// name
std::size_t len = 0;
while (len < sizeof(ctrl.name) && ctrl.name[len] != '\0') ++len;
llvm::SmallString<64> name_buf;
name = NormalizeName(
llvm::StringRef(reinterpret_cast<const char*>(ctrl.name), len), name_buf);
}
static inline int CheckedIoctl(int fd, unsigned long req, void* data,
const char* name, const char* file, int line,
bool quiet) {
int retval = ioctl(fd, req, data);
if (!quiet && retval < 0) {
llvm::SmallString<64> localfile{file};
localfile.push_back('\0');
ERROR("ioctl " << name << " failed at " << basename(localfile.data()) << ":"
<< line << ": " << std::strerror(errno));
}
return retval;
}
#define DoIoctl(fd, req, data) \
CheckedIoctl(fd, req, data, #req, __FILE__, __LINE__, false)
#define TryIoctl(fd, req, data) \
CheckedIoctl(fd, req, data, #req, __FILE__, __LINE__, true)
static std::unique_ptr<UsbCameraImpl::PropertyData> ExtCtrlIoctl(int fd,
__u32* id) {
static std::unique_ptr<UsbCameraProperty> ExtCtrlIoctl(int fd, __u32* id) {
int rc;
std::unique_ptr<UsbCameraImpl::PropertyData> prop;
std::unique_ptr<UsbCameraProperty> prop;
#ifdef VIDIOC_QUERY_EXT_CTRL
v4l2_query_ext_ctrl qc_ext;
std::memset(&qc_ext, 0, sizeof(qc_ext));
@@ -231,7 +119,7 @@ static std::unique_ptr<UsbCameraImpl::PropertyData> ExtCtrlIoctl(int fd,
*id = qc_ext.id; // copy back
// We don't support array types
if (qc_ext.elems > 1 || qc_ext.nr_of_dims > 0) return nullptr;
prop = llvm::make_unique<UsbCameraImpl::PropertyData>(qc_ext);
prop = llvm::make_unique<UsbCameraProperty>(qc_ext);
}
#endif
if (!prop) {
@@ -242,7 +130,7 @@ static std::unique_ptr<UsbCameraImpl::PropertyData> ExtCtrlIoctl(int fd,
rc = TryIoctl(fd, VIDIOC_QUERYCTRL, &qc);
*id = qc.id; // copy back
if (rc != 0) return nullptr;
prop = llvm::make_unique<UsbCameraImpl::PropertyData>(qc);
prop = llvm::make_unique<UsbCameraProperty>(qc);
}
// Cache enum property choices
@@ -261,100 +149,6 @@ static std::unique_ptr<UsbCameraImpl::PropertyData> ExtCtrlIoctl(int fd,
return prop;
}
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));
std::memset(&ctrls, 0, sizeof(ctrls));
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;
std::memset(&ctrl, 0, sizeof(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));
std::memset(&ctrls, 0, sizeof(ctrls));
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 int GetStringCtrlIoctl(int fd, int id, int maximum,
std::string* value) {
struct v4l2_ext_control ctrl;
struct v4l2_ext_controls ctrls;
std::memset(&ctrl, 0, sizeof(ctrl));
std::memset(&ctrls, 0, sizeof(ctrls));
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) {
value->clear();
return rc;
}
value->assign(ctrl.string, std::strlen(ctrl.string));
return 0;
}
static int SetStringCtrlIoctl(int fd, int id, int maximum,
llvm::StringRef value) {
llvm::SmallString<64> str{value.substr(
0, std::min(value.size(), static_cast<std::size_t>(maximum)))};
struct v4l2_ext_control ctrl;
struct v4l2_ext_controls ctrls;
std::memset(&ctrl, 0, sizeof(ctrl));
std::memset(&ctrls, 0, sizeof(ctrls));
ctrl.id = id;
ctrl.size = str.size();
ctrl.string = const_cast<char*>(str.c_str());
ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(id);
ctrls.count = 1;
ctrls.controls = &ctrl;
return DoIoctl(fd, VIDIOC_S_EXT_CTRLS, &ctrls);
}
static bool GetDescriptionSysV4L(llvm::StringRef path, std::string* desc) {
llvm::SmallString<64> ifpath{"/sys/class/video4linux/"};
ifpath += path.substr(5);
@@ -694,9 +488,9 @@ void UsbCameraImpl::DeviceConnect() {
std::unique_lock<std::mutex> lock2(m_mutex);
for (std::size_t i = 0; i < m_propertyData.size(); ++i) {
const auto prop =
static_cast<const PropertyData*>(m_propertyData[i].get());
static_cast<const UsbCameraProperty*>(m_propertyData[i].get());
if (!prop || !prop->valueSet || prop->percentage) continue;
if (!DeviceSetProperty(lock2, *prop))
if (!prop->DeviceSet(lock2, m_fd))
SWARNING("failed to set property " << prop->name);
}
}
@@ -1040,7 +834,8 @@ void UsbCameraImpl::DeviceCacheMode() {
Notifier::GetInstance().NotifySource(*this, CS_SOURCE_VIDEOMODE_CHANGED);
}
void UsbCameraImpl::DeviceCacheProperty(std::unique_ptr<PropertyData> rawProp) {
void UsbCameraImpl::DeviceCacheProperty(
std::unique_ptr<UsbCameraProperty> rawProp) {
// For percentage properties, we want to cache both the raw and the
// percentage versions. This function is always called with prop being
// the raw property (as it's coming from the camera) so if required, we need
@@ -1049,22 +844,25 @@ void UsbCameraImpl::DeviceCacheProperty(std::unique_ptr<PropertyData> rawProp) {
// This is complicated by the fact that either the percentage version or the
// the raw version may have been set previously. If both were previously set,
// the raw version wins.
std::unique_ptr<PropertyData> perProp;
std::unique_ptr<UsbCameraProperty> perProp;
if (IsPercentageProperty(rawProp->name)) {
perProp = llvm::make_unique<PropertyData>(rawProp->name, 0, *rawProp, 0, 0);
perProp =
llvm::make_unique<UsbCameraProperty>(rawProp->name, 0, *rawProp, 0, 0);
rawProp->name = "raw_" + perProp->name;
}
std::unique_lock<std::mutex> lock(m_mutex);
int* rawIndex = &m_properties[rawProp->name];
bool newRaw = *rawIndex == 0;
PropertyData* oldRawProp =
newRaw ? nullptr : static_cast<PropertyData*>(GetProperty(*rawIndex));
UsbCameraProperty* oldRawProp =
newRaw ? nullptr
: static_cast<UsbCameraProperty*>(GetProperty(*rawIndex));
int* perIndex = perProp ? &m_properties[perProp->name] : nullptr;
bool newPer = !perIndex || *perIndex == 0;
PropertyData* oldPerProp =
newPer ? nullptr : static_cast<PropertyData*>(GetProperty(*perIndex));
UsbCameraProperty* oldPerProp =
newPer ? nullptr
: static_cast<UsbCameraProperty*>(GetProperty(*perIndex));
if (oldRawProp && oldRawProp->valueSet) {
// Merge existing raw setting and set percentage from it
@@ -1084,10 +882,8 @@ void UsbCameraImpl::DeviceCacheProperty(std::unique_ptr<PropertyData> rawProp) {
rawProp->valueStr = perProp->valueStr; // copy
} else {
// Read current raw value and set percentage from it
lock.unlock();
if (!DeviceGetProperty(rawProp.get()))
if (!rawProp->DeviceGet(lock, m_fd))
SWARNING("failed to get property " << rawProp->name);
lock.lock();
if (perProp) {
perProp->SetValue(RawToPercentage(*rawProp, rawProp->value));
@@ -1097,7 +893,7 @@ void UsbCameraImpl::DeviceCacheProperty(std::unique_ptr<PropertyData> rawProp) {
// Set value on device if user-configured
if (rawProp->valueSet) {
if (!DeviceSetProperty(lock, *rawProp))
if (!rawProp->DeviceSet(lock, m_fd))
SWARNING("failed to set property " << rawProp->name);
}
@@ -1164,7 +960,7 @@ void UsbCameraImpl::DeviceCacheProperties() {
DeviceCacheProperty(std::move(prop));
}
// ... and custom controls
std::unique_ptr<PropertyData> prop;
std::unique_ptr<UsbCameraProperty> prop;
for (id = V4L2_CID_PRIVATE_BASE; (prop = ExtCtrlIoctl(fd, &id)); ++id)
DeviceCacheProperty(std::move(prop));
}
@@ -1218,77 +1014,11 @@ void UsbCameraImpl::DeviceCacheVideoModes() {
Notifier::GetInstance().NotifySource(*this, CS_SOURCE_VIDEOMODES_UPDATED);
}
bool UsbCameraImpl::DeviceGetProperty(PropertyData* prop) {
int fd = m_fd.load();
if (fd < 0) return true;
int rv = 0;
switch (prop->propKind) {
case CS_PROP_BOOLEAN:
case CS_PROP_INTEGER:
case CS_PROP_ENUM:
{
int64_t value = 0;
rv = GetIntCtrlIoctl(fd, prop->id, prop->type, &value);
if (rv >= 0) prop->value = value;
}
break;
case CS_PROP_STRING:
rv = GetStringCtrlIoctl(fd, prop->id, prop->maximum, &prop->valueStr);
break;
default:
break;
}
return rv >= 0;
}
bool UsbCameraImpl::DeviceSetProperty(std::unique_lock<std::mutex>& lock,
const PropertyData& prop) {
// Make a copy of the string as we're about to release the lock
llvm::SmallString<128> valueStr{prop.valueStr};
return DeviceSetProperty(lock, prop, prop.value, valueStr);
}
bool UsbCameraImpl::DeviceSetProperty(std::unique_lock<std::mutex>& lock,
const PropertyData& prop, int value,
llvm::StringRef valueStr) {
int fd = m_fd.load();
if (fd < 0) return true;
unsigned id = prop.id;
int rv = 0;
switch (prop.propKind) {
case CS_PROP_BOOLEAN:
case CS_PROP_INTEGER:
case CS_PROP_ENUM:
{
int type = prop.type;
lock.unlock();
rv = SetIntCtrlIoctl(fd, id, type, value);
lock.lock();
}
break;
case CS_PROP_STRING:
{
int maximum = prop.maximum;
lock.unlock();
rv = SetStringCtrlIoctl(fd, id, maximum, valueStr);
lock.lock();
}
break;
default:
break;
}
return rv >= 0;
}
CS_StatusValue UsbCameraImpl::DeviceCmdSetProperty(
std::unique_lock<std::mutex>& lock, int property, bool setString, int value,
llvm::StringRef valueStr) {
// Look up
auto prop = static_cast<PropertyData*>(GetProperty(property));
auto prop = static_cast<UsbCameraProperty*>(GetProperty(property));
if (!prop) return CS_INVALID_PROPERTY;
// If setting before we get, guess initial type based on set
@@ -1312,7 +1042,7 @@ CS_StatusValue UsbCameraImpl::DeviceCmdSetProperty(
if (percentageProperty != 0) {
if (prop->percentage) {
std::swap(percentageProperty, property);
prop = static_cast<PropertyData*>(GetProperty(property));
prop = static_cast<UsbCameraProperty*>(GetProperty(property));
value = PercentageToRaw(*prop, percentageValue);
} else {
percentageValue = RawToPercentage(*prop, value);
@@ -1320,7 +1050,7 @@ CS_StatusValue UsbCameraImpl::DeviceCmdSetProperty(
}
// Actually set the new value on the device (if possible)
if (!DeviceSetProperty(lock, *prop, value, valueStr))
if (!prop->DeviceSet(lock, m_fd, value, valueStr))
return CS_PROPERTY_WRITE_FAILED;
// Cache the set values
@@ -1391,7 +1121,7 @@ void UsbCameraImpl::Send(std::unique_ptr<Message> msg) const {
std::unique_ptr<PropertyImpl> UsbCameraImpl::CreateEmptyProperty(
llvm::StringRef name) const {
return llvm::make_unique<PropertyData>(name);
return llvm::make_unique<UsbCameraProperty>(name);
}
bool UsbCameraImpl::CacheProperties(CS_Status* status) const {

View File

@@ -23,6 +23,7 @@
#include "SourceImpl.h"
#include "UsbCameraBuffer.h"
#include "UsbCameraProperty.h"
namespace cs {
@@ -49,43 +50,6 @@ class UsbCameraImpl : public SourceImpl {
std::string GetPath() { return m_path; }
// Property data
class PropertyData : public PropertyImpl {
public:
PropertyData() = default;
PropertyData(llvm::StringRef name_) : PropertyImpl{name_} {}
// Normalized property constructor
PropertyData(llvm::StringRef name_, int rawIndex_,
const PropertyData& rawProp, int defaultValue_, int value_)
: PropertyImpl(name_, rawProp.propKind, 1, defaultValue_, value_),
percentage{true},
propPair{rawIndex_},
id{rawProp.id},
type{rawProp.type} {
hasMinimum = true;
minimum = 0;
hasMaximum = true;
maximum = 100;
}
#ifdef __linux__
#ifdef VIDIOC_QUERY_EXT_CTRL
PropertyData(const struct v4l2_query_ext_ctrl& ctrl);
#endif
PropertyData(const struct v4l2_queryctrl& ctrl);
#endif
// If this is a percentage (rather than raw) property
bool percentage{false};
// If not 0, index of corresponding raw/percentage property
int propPair{0};
unsigned id{0}; // implementation-level id
int type{0}; // implementation type, not CS_PropertyKind!
};
// Messages passed to/from camera thread
struct Message {
enum Kind {
@@ -151,15 +115,9 @@ class UsbCameraImpl : public SourceImpl {
void DeviceSetMode();
void DeviceSetFPS();
void DeviceCacheMode();
void DeviceCacheProperty(std::unique_ptr<PropertyData> rawProp);
void DeviceCacheProperty(std::unique_ptr<UsbCameraProperty> rawProp);
void DeviceCacheProperties();
void DeviceCacheVideoModes();
bool DeviceGetProperty(PropertyData* prop);
bool DeviceSetProperty(std::unique_lock<std::mutex>& lock,
const PropertyData& prop);
bool DeviceSetProperty(std::unique_lock<std::mutex>& lock,
const PropertyData& prop, int value,
llvm::StringRef valueStr);
// Command helper functions
CS_StatusValue DeviceCmdSetProperty(std::unique_lock<std::mutex>& lock,
@@ -167,8 +125,8 @@ class UsbCameraImpl : public SourceImpl {
llvm::StringRef valueStr);
// Property helper functions
int RawToPercentage(const PropertyData& rawProp, int rawValue);
int PercentageToRaw(const PropertyData& rawProp, int percentValue);
int RawToPercentage(const UsbCameraProperty& rawProp, int rawValue);
int PercentageToRaw(const UsbCameraProperty& rawProp, int percentValue);
//
// Variables only used within camera thread

277
src/UsbCameraProperty.cpp Normal file
View File

@@ -0,0 +1,277 @@
/*----------------------------------------------------------------------------*/
/* 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 "UsbCameraProperty.h"
#include "llvm/SmallString.h"
#include "UsbUtil.h"
using namespace cs;
#ifdef __linux__
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));
std::memset(&ctrls, 0, sizeof(ctrls));
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;
std::memset(&ctrl, 0, sizeof(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));
std::memset(&ctrls, 0, sizeof(ctrls));
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 int GetStringCtrlIoctl(int fd, int id, int maximum,
std::string* value) {
struct v4l2_ext_control ctrl;
struct v4l2_ext_controls ctrls;
std::memset(&ctrl, 0, sizeof(ctrl));
std::memset(&ctrls, 0, sizeof(ctrls));
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) {
value->clear();
return rc;
}
value->assign(ctrl.string, std::strlen(ctrl.string));
return 0;
}
static int SetStringCtrlIoctl(int fd, int id, int maximum,
llvm::StringRef value) {
llvm::SmallString<64> str{value.substr(
0, std::min(value.size(), static_cast<std::size_t>(maximum)))};
struct v4l2_ext_control ctrl;
struct v4l2_ext_controls ctrls;
std::memset(&ctrl, 0, sizeof(ctrl));
std::memset(&ctrls, 0, sizeof(ctrls));
ctrl.id = id;
ctrl.size = str.size();
ctrl.string = const_cast<char*>(str.c_str());
ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(id);
ctrls.count = 1;
ctrls.controls = &ctrl;
return DoIoctl(fd, VIDIOC_S_EXT_CTRLS, &ctrls);
}
// Removes non-alphanumeric characters and replaces spaces with underscores.
// e.g. "Zoom, Absolute" -> "zoom_absolute", "Pan (Absolute)" -> "pan_absolute"
static llvm::StringRef NormalizeName(llvm::StringRef name,
llvm::SmallVectorImpl<char>& buf) {
bool newWord = false;
for (auto ch : name) {
if (std::isalnum(ch)) {
if (newWord) buf.push_back('_');
newWord = false;
buf.push_back(std::tolower(ch));
} else if (!buf.empty())
newWord = true;
}
return llvm::StringRef(buf.data(), buf.size());
}
#ifdef VIDIOC_QUERY_EXT_CTRL
UsbCameraProperty::UsbCameraProperty(const struct v4l2_query_ext_ctrl& ctrl)
: PropertyImpl(llvm::StringRef{}, CS_PROP_NONE, ctrl.step,
ctrl.default_value, 0),
id(ctrl.id & V4L2_CTRL_ID_MASK),
type(ctrl.type) {
hasMinimum = true;
minimum = ctrl.minimum;
hasMaximum = true;
maximum = ctrl.maximum;
// propKind
switch (ctrl.type) {
case V4L2_CTRL_TYPE_INTEGER:
case V4L2_CTRL_TYPE_INTEGER64:
propKind = CS_PROP_INTEGER;
break;
case V4L2_CTRL_TYPE_BOOLEAN:
propKind = CS_PROP_BOOLEAN;
break;
case V4L2_CTRL_TYPE_INTEGER_MENU:
case V4L2_CTRL_TYPE_MENU:
propKind = CS_PROP_ENUM;
break;
case V4L2_CTRL_TYPE_STRING:
propKind = CS_PROP_STRING;
break;
default:
return; // others unsupported
}
// name
std::size_t len = 0;
while (len < sizeof(ctrl.name) && ctrl.name[len] != '\0') ++len;
llvm::SmallString<64> name_buf;
name = NormalizeName(llvm::StringRef(ctrl.name, len), name_buf);
}
#endif
UsbCameraProperty::UsbCameraProperty(const struct v4l2_queryctrl& ctrl)
: PropertyImpl(llvm::StringRef{}, CS_PROP_NONE, ctrl.step,
ctrl.default_value, 0),
id(ctrl.id & V4L2_CTRL_ID_MASK),
type(ctrl.type) {
hasMinimum = true;
minimum = ctrl.minimum;
hasMaximum = true;
maximum = ctrl.maximum;
// propKind
switch (ctrl.type) {
case V4L2_CTRL_TYPE_INTEGER:
case V4L2_CTRL_TYPE_INTEGER64:
propKind = CS_PROP_INTEGER;
break;
case V4L2_CTRL_TYPE_BOOLEAN:
propKind = CS_PROP_BOOLEAN;
break;
case V4L2_CTRL_TYPE_INTEGER_MENU:
case V4L2_CTRL_TYPE_MENU:
propKind = CS_PROP_ENUM;
break;
case V4L2_CTRL_TYPE_STRING:
propKind = CS_PROP_STRING;
break;
default:
return; // others unsupported
}
// name
std::size_t len = 0;
while (len < sizeof(ctrl.name) && ctrl.name[len] != '\0') ++len;
llvm::SmallString<64> name_buf;
name = NormalizeName(
llvm::StringRef(reinterpret_cast<const char*>(ctrl.name), len), name_buf);
}
bool UsbCameraProperty::DeviceGet(std::unique_lock<std::mutex>& lock, int fd) {
if (fd < 0) return true;
unsigned idCopy = id;
int rv = 0;
switch (propKind) {
case CS_PROP_BOOLEAN:
case CS_PROP_INTEGER:
case CS_PROP_ENUM: {
int typeCopy = type;
int64_t newValue = 0;
lock.unlock();
rv = GetIntCtrlIoctl(fd, idCopy, typeCopy, &newValue);
lock.lock();
if (rv >= 0) value = newValue;
break;
}
case CS_PROP_STRING: {
int maximumCopy = maximum;
std::string newValueStr;
lock.unlock();
rv = GetStringCtrlIoctl(fd, idCopy, maximumCopy, &newValueStr);
lock.lock();
if (rv >= 0) valueStr = std::move(newValueStr);
break;
}
default:
break;
}
return rv >= 0;
}
bool UsbCameraProperty::DeviceSet(std::unique_lock<std::mutex>& lock,
int fd) const {
// Make a copy of the string as we're about to release the lock
llvm::SmallString<128> valueStrCopy{valueStr};
return DeviceSet(lock, fd, value, valueStrCopy);
}
bool UsbCameraProperty::DeviceSet(std::unique_lock<std::mutex>& lock, int fd,
int newValue,
llvm::StringRef newValueStr) const {
if (fd < 0) return true;
unsigned idCopy = id;
int rv = 0;
switch (propKind) {
case CS_PROP_BOOLEAN:
case CS_PROP_INTEGER:
case CS_PROP_ENUM: {
int typeCopy = type;
lock.unlock();
rv = SetIntCtrlIoctl(fd, idCopy, typeCopy, newValue);
lock.lock();
break;
}
case CS_PROP_STRING: {
int maximumCopy = maximum;
lock.unlock();
rv = SetStringCtrlIoctl(fd, idCopy, maximumCopy, newValueStr);
lock.lock();
break;
}
default:
break;
}
return rv >= 0;
}
#endif // __linux__

66
src/UsbCameraProperty.h Normal file
View File

@@ -0,0 +1,66 @@
/*----------------------------------------------------------------------------*/
/* 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 CS_USBCAMERAPROPERTY_H_
#define CS_USBCAMERAPROPERTY_H_
#include <mutex>
#ifdef __linux__
#include <linux/videodev2.h>
#endif
#include "PropertyImpl.h"
namespace cs {
// Property data
class UsbCameraProperty : public PropertyImpl {
public:
UsbCameraProperty() = default;
UsbCameraProperty(llvm::StringRef name_) : PropertyImpl{name_} {}
// Normalized property constructor
UsbCameraProperty(llvm::StringRef name_, int rawIndex_,
const UsbCameraProperty& rawProp, int defaultValue_,
int value_)
: PropertyImpl(name_, rawProp.propKind, 1, defaultValue_, value_),
percentage{true},
propPair{rawIndex_},
id{rawProp.id},
type{rawProp.type} {
hasMinimum = true;
minimum = 0;
hasMaximum = true;
maximum = 100;
}
#ifdef __linux__
#ifdef VIDIOC_QUERY_EXT_CTRL
UsbCameraProperty(const struct v4l2_query_ext_ctrl& ctrl);
#endif
UsbCameraProperty(const struct v4l2_queryctrl& ctrl);
bool DeviceGet(std::unique_lock<std::mutex>& lock, int fd);
bool DeviceSet(std::unique_lock<std::mutex>& lock, int fd) const;
bool DeviceSet(std::unique_lock<std::mutex>& lock, int fd, int newValue,
llvm::StringRef newValueStr) const;
#endif
// If this is a percentage (rather than raw) property
bool percentage{false};
// If not 0, index of corresponding raw/percentage property
int propPair{0};
unsigned id{0}; // implementation-level id
int type{0}; // implementation type, not CS_PropertyKind!
};
} // namespace cs
#endif // CS_USBCAMERAPROPERTY_H_

View File

@@ -8,6 +8,10 @@
#include "UsbUtil.h"
#include <fcntl.h>
#ifdef __linux__
#include <libgen.h>
#include <sys/ioctl.h>
#endif
#include "llvm/Format.h"
#include "llvm/SmallString.h"
@@ -15,6 +19,7 @@
#include "support/raw_istream.h"
#include "HttpUtil.h"
#include "Log.h"
namespace cs {
@@ -105,4 +110,20 @@ llvm::StringRef GetUsbNameFromId(int vendor, int product,
return os.str();
}
#ifdef __linux__
int CheckedIoctl(int fd, unsigned long req, void* data, const char* name,
const char* file, int line, bool quiet) {
int retval = ioctl(fd, req, data);
if (!quiet && retval < 0) {
llvm::SmallString<64> localfile{file};
localfile.push_back('\0');
ERROR("ioctl " << name << " failed at " << basename(localfile.data()) << ":"
<< line << ": " << std::strerror(errno));
}
return retval;
}
#endif // __linux__
} // namespace cs

View File

@@ -16,6 +16,17 @@ namespace cs {
llvm::StringRef GetUsbNameFromId(int vendor, int product,
llvm::SmallVectorImpl<char>& buf);
#ifdef __linux__
int CheckedIoctl(int fd, unsigned long req, void* data, const char* name,
const char* file, int line, bool quiet);
#define DoIoctl(fd, req, data) \
CheckedIoctl(fd, req, data, #req, __FILE__, __LINE__, false)
#define TryIoctl(fd, req, data) \
CheckedIoctl(fd, req, data, #req, __FILE__, __LINE__, true)
#endif // __linux__
} // namespace cs
#endif // CS_USBUTIL_H_