mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-07-01 02:41:48 +00:00
UsbCamera: Scale some properties to make them constently percentages.
The "raw" version of these properties are still available, just prefixed with "raw_".
This commit is contained in:
@@ -120,6 +120,29 @@ def cscoreSetupExamplesModel = { project ->
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
settings(NativeExecutableSpec) {
|
||||
if (project.isArm) {
|
||||
targetPlatform 'arm'
|
||||
} else {
|
||||
//targetPlatform 'x86'
|
||||
targetPlatform 'x64'
|
||||
}
|
||||
setupDefines(project, binaries)
|
||||
sources {
|
||||
cpp {
|
||||
source {
|
||||
srcDir "${rootDir}/examples/settings"
|
||||
include '**/*.cpp'
|
||||
}
|
||||
exportedHeaders {
|
||||
srcDirs = ["${rootDir}/include", "${rootDir}/wpiutil/include", project.openCvInclude]
|
||||
include '**/*.h'
|
||||
}
|
||||
lib library: 'cscore', linkage: 'static'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
95
examples/settings/settings.cpp
Normal file
95
examples/settings/settings.cpp
Normal file
@@ -0,0 +1,95 @@
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include "cscore.h"
|
||||
|
||||
#include "llvm/SmallString.h"
|
||||
#include "llvm/raw_ostream.h"
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc < 2) {
|
||||
llvm::errs() << "Usage: settings camera [prop val] ... -- [prop val]...\n";
|
||||
llvm::errs() << " Example: settings 1 brightness 30 raw_contrast 10\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
int id;
|
||||
if (llvm::StringRef{argv[1]}.getAsInteger(10, id)) {
|
||||
llvm::errs() << "Expected number for camera\n";
|
||||
return 2;
|
||||
}
|
||||
|
||||
cs::UsbCamera camera{"usbcam", id};
|
||||
|
||||
// Set prior to connect
|
||||
int arg = 2;
|
||||
llvm::StringRef propName;
|
||||
for (; arg < argc && llvm::StringRef{argv[arg]} != "--"; ++arg) {
|
||||
if (propName.empty())
|
||||
propName = argv[arg];
|
||||
else {
|
||||
llvm::StringRef propVal{argv[arg]};
|
||||
int intVal;
|
||||
if (propVal.getAsInteger(10, intVal))
|
||||
camera.GetProperty(propName).SetString(propVal);
|
||||
else
|
||||
camera.GetProperty(propName).Set(intVal);
|
||||
propName = llvm::StringRef{};
|
||||
}
|
||||
}
|
||||
if (llvm::StringRef{argv[arg]} == "--") ++arg;
|
||||
|
||||
// Wait to connect
|
||||
while (!camera.IsConnected())
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
|
||||
// Set rest
|
||||
propName = llvm::StringRef{};
|
||||
for (; arg < argc; ++arg) {
|
||||
if (propName.empty())
|
||||
propName = argv[arg];
|
||||
else {
|
||||
llvm::StringRef propVal{argv[arg]};
|
||||
int intVal;
|
||||
if (propVal.getAsInteger(10, intVal))
|
||||
camera.GetProperty(propName).SetString(propVal);
|
||||
else
|
||||
camera.GetProperty(propName).Set(intVal);
|
||||
propName = llvm::StringRef{};
|
||||
}
|
||||
}
|
||||
|
||||
// Print settings
|
||||
llvm::SmallString<64> buf;
|
||||
llvm::outs() << "Properties:\n";
|
||||
for (const auto& prop : camera.EnumerateProperties()) {
|
||||
llvm::outs() << " " << prop.GetName();
|
||||
switch (prop.GetKind()) {
|
||||
case cs::VideoProperty::kBoolean:
|
||||
llvm::outs() << " (bool): " << "value=" << prop.Get()
|
||||
<< " default=" << prop.GetDefault();
|
||||
break;
|
||||
case cs::VideoProperty::kInteger:
|
||||
llvm::outs() << " (int): "
|
||||
<< "value=" << prop.Get() << " min=" << prop.GetMin()
|
||||
<< " max=" << prop.GetMax() << " step=" << prop.GetStep()
|
||||
<< " default=" << prop.GetDefault();
|
||||
break;
|
||||
case cs::VideoProperty::kString:
|
||||
llvm::outs() << " (string): " << prop.GetString(buf);
|
||||
break;
|
||||
case cs::VideoProperty::kEnum: {
|
||||
llvm::outs() << " (enum): " << "value=" << prop.Get();
|
||||
auto choices = prop.GetChoices();
|
||||
for (size_t i = 0; i < choices.size(); ++i) {
|
||||
if (choices[i].empty()) continue;
|
||||
llvm::outs() << "\n " << i << ": " << choices[i];
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
llvm::outs() << '\n';
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,11 @@ CvSourceImpl::~CvSourceImpl() {}
|
||||
|
||||
void CvSourceImpl::Start() {}
|
||||
|
||||
std::unique_ptr<SourceImpl::PropertyBase> CvSourceImpl::CreateEmptyProperty(
|
||||
llvm::StringRef name) const {
|
||||
return llvm::make_unique<PropertyData>(name);
|
||||
}
|
||||
|
||||
bool CvSourceImpl::CacheProperties(CS_Status* status) const {
|
||||
// Doesn't need to do anything.
|
||||
m_properties_cached = true;
|
||||
@@ -43,15 +48,20 @@ void CvSourceImpl::SetProperty(int property, int value, CS_Status* status) {
|
||||
*status = CS_INVALID_PROPERTY;
|
||||
return;
|
||||
}
|
||||
|
||||
// Guess it's integer if we've set before get
|
||||
if (prop->propKind == CS_PROP_NONE) prop->propKind = CS_PROP_INTEGER;
|
||||
|
||||
if ((prop->propKind & (CS_PROP_BOOLEAN | CS_PROP_INTEGER | CS_PROP_ENUM)) ==
|
||||
0) {
|
||||
*status = CS_WRONG_PROPERTY_TYPE;
|
||||
return;
|
||||
}
|
||||
prop->value = value;
|
||||
Notifier::GetInstance().NotifySourceProperty(
|
||||
*this, CS_SOURCE_PROPERTY_VALUE_UPDATED, property, prop->propKind,
|
||||
prop->value, prop->valueStr);
|
||||
prop->SetValue(value);
|
||||
if (m_properties_cached)
|
||||
Notifier::GetInstance().NotifySourceProperty(
|
||||
*this, CS_SOURCE_PROPERTY_VALUE_UPDATED, property, prop->propKind,
|
||||
prop->value, prop->valueStr);
|
||||
}
|
||||
|
||||
void CvSourceImpl::SetStringProperty(int property, llvm::StringRef value,
|
||||
@@ -62,14 +72,19 @@ void CvSourceImpl::SetStringProperty(int property, llvm::StringRef value,
|
||||
*status = CS_INVALID_PROPERTY;
|
||||
return;
|
||||
}
|
||||
|
||||
// Guess it's string if we've set before get
|
||||
if (prop->propKind == CS_PROP_NONE) prop->propKind = CS_PROP_STRING;
|
||||
|
||||
if (prop->propKind != CS_PROP_STRING) {
|
||||
*status = CS_WRONG_PROPERTY_TYPE;
|
||||
return;
|
||||
}
|
||||
prop->valueStr = value;
|
||||
Notifier::GetInstance().NotifySourceProperty(
|
||||
*this, CS_SOURCE_PROPERTY_VALUE_UPDATED, property, CS_PROP_STRING,
|
||||
prop->value, prop->valueStr);
|
||||
prop->SetValue(value);
|
||||
if (m_properties_cached)
|
||||
Notifier::GetInstance().NotifySourceProperty(
|
||||
*this, CS_SOURCE_PROPERTY_VALUE_UPDATED, property, CS_PROP_STRING,
|
||||
prop->value, prop->valueStr);
|
||||
}
|
||||
|
||||
bool CvSourceImpl::SetVideoMode(const VideoMode& mode, CS_Status* status) {
|
||||
|
||||
@@ -48,16 +48,24 @@ class CvSourceImpl : public SourceImpl {
|
||||
class PropertyData : public PropertyBase {
|
||||
public:
|
||||
PropertyData() = default;
|
||||
PropertyData(llvm::StringRef name_) : PropertyBase{name_} {}
|
||||
PropertyData(llvm::StringRef name_, CS_PropertyKind kind_, int minimum_,
|
||||
int maximum_, int step_, int defaultValue_, int value_)
|
||||
: PropertyBase{name_, kind_, minimum_, maximum_,
|
||||
step_, defaultValue_, value_} {}
|
||||
: PropertyBase{name_, kind_, step_, defaultValue_, value_} {
|
||||
hasMinimum = true;
|
||||
minimum = minimum_;
|
||||
hasMaximum = true;
|
||||
maximum = maximum_;
|
||||
}
|
||||
~PropertyData() override = default;
|
||||
|
||||
std::function<void(CS_Property property)> onChange;
|
||||
};
|
||||
|
||||
protected:
|
||||
std::unique_ptr<PropertyBase> CreateEmptyProperty(
|
||||
llvm::StringRef name) const override;
|
||||
|
||||
bool CacheProperties(CS_Status* status) const override;
|
||||
|
||||
private:
|
||||
|
||||
@@ -90,7 +90,7 @@ int SourceImpl::GetPropertyIndex(llvm::StringRef name) const {
|
||||
if (ndx == 0) {
|
||||
// create a new index
|
||||
ndx = m_propertyData.size() + 1;
|
||||
m_propertyData.emplace_back();
|
||||
m_propertyData.emplace_back(CreateEmptyProperty(name));
|
||||
}
|
||||
return ndx;
|
||||
}
|
||||
|
||||
@@ -139,12 +139,11 @@ class SourceImpl {
|
||||
class PropertyBase {
|
||||
public:
|
||||
PropertyBase() = default;
|
||||
PropertyBase(llvm::StringRef name_, CS_PropertyKind kind_, int minimum_,
|
||||
int maximum_, int step_, int defaultValue_, int value_)
|
||||
PropertyBase(llvm::StringRef name_) : name{name_} {}
|
||||
PropertyBase(llvm::StringRef name_, CS_PropertyKind kind_, int step_,
|
||||
int defaultValue_, int value_)
|
||||
: name{name_},
|
||||
propKind{kind_},
|
||||
minimum{minimum_},
|
||||
maximum{maximum_},
|
||||
step{step_},
|
||||
defaultValue{defaultValue_},
|
||||
value{value_} {}
|
||||
@@ -152,12 +151,38 @@ class SourceImpl {
|
||||
PropertyBase(const PropertyBase& oth) = delete;
|
||||
PropertyBase& operator=(const PropertyBase& oth) = delete;
|
||||
|
||||
void SetValue(int v) {
|
||||
if (hasMinimum && v < minimum)
|
||||
value = minimum;
|
||||
else if (hasMaximum && v > maximum)
|
||||
value = maximum;
|
||||
else
|
||||
value = v;
|
||||
valueSet = true;
|
||||
}
|
||||
|
||||
void SetValue(llvm::StringRef v) {
|
||||
valueStr = v;
|
||||
valueSet = true;
|
||||
}
|
||||
|
||||
void SetDefaultValue(int v) {
|
||||
if (hasMinimum && v < minimum)
|
||||
defaultValue = minimum;
|
||||
else if (hasMaximum && v > maximum)
|
||||
defaultValue = maximum;
|
||||
else
|
||||
defaultValue = v;
|
||||
}
|
||||
|
||||
std::string name;
|
||||
CS_PropertyKind propKind{CS_PROP_NONE};
|
||||
int minimum;
|
||||
int maximum;
|
||||
int step;
|
||||
int defaultValue;
|
||||
bool hasMinimum{false};
|
||||
bool hasMaximum{false};
|
||||
int minimum{0};
|
||||
int maximum{100};
|
||||
int step{1};
|
||||
int defaultValue{0};
|
||||
int value{0};
|
||||
std::string valueStr;
|
||||
std::vector<std::string> enumChoices;
|
||||
@@ -176,6 +201,12 @@ class SourceImpl {
|
||||
return m_propertyData[property - 1].get();
|
||||
}
|
||||
|
||||
// Create an "empty" property. This is called by GetPropertyIndex to create
|
||||
// properties that don't exist (as GetPropertyIndex can't fail).
|
||||
// Note: called with m_mutex held.
|
||||
virtual std::unique_ptr<PropertyBase> CreateEmptyProperty(
|
||||
llvm::StringRef name) const = 0;
|
||||
|
||||
// Cache properties. Implementations must return false and set status to
|
||||
// CS_SOURCE_IS_DISCONNECTED if not possible to cache.
|
||||
virtual bool CacheProperties(CS_Status* status) const = 0;
|
||||
|
||||
@@ -57,19 +57,37 @@ static inline struct v4l2_fract FPSToFract(int fps) {
|
||||
}
|
||||
|
||||
// Conversion from v4l2_format pixelformat to VideoMode::PixelFormat
|
||||
static VideoMode::PixelFormat ToPixelFormat(__u32 pixelformat) {
|
||||
switch (pixelformat) {
|
||||
static VideoMode::PixelFormat ToPixelFormat(__u32 pixelFormat) {
|
||||
switch (pixelFormat) {
|
||||
case V4L2_PIX_FMT_MJPEG:
|
||||
return VideoMode::kMJPEG;
|
||||
case V4L2_PIX_FMT_YUYV:
|
||||
return VideoMode::kYUYV;
|
||||
case V4L2_PIX_FMT_RGB565:
|
||||
return VideoMode::kRGB565;
|
||||
case V4L2_PIX_FMT_BGR24:
|
||||
return VideoMode::kBGR;
|
||||
default:
|
||||
return VideoMode::kUnknown;
|
||||
}
|
||||
}
|
||||
|
||||
// Conversion from VideoMode::PixelFormat to v4l2_format pixelformat
|
||||
static __u32 FromPixelFormat(VideoMode::PixelFormat pixelFormat) {
|
||||
switch (pixelFormat) {
|
||||
case VideoMode::kMJPEG:
|
||||
return V4L2_PIX_FMT_MJPEG;
|
||||
case VideoMode::kYUYV:
|
||||
return V4L2_PIX_FMT_YUYV;
|
||||
case VideoMode::kRGB565:
|
||||
return V4L2_PIX_FMT_RGB565;
|
||||
case VideoMode::kBGR:
|
||||
return V4L2_PIX_FMT_BGR24;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 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,
|
||||
@@ -86,13 +104,53 @@ static llvm::StringRef NormalizeName(llvm::StringRef name,
|
||||
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" ||
|
||||
name == "hue" || name == "sharpness" || name == "gain" ||
|
||||
name == "exposure_absolute";
|
||||
}
|
||||
|
||||
int UsbCameraImpl::RawToPercentage(const PropertyData& rawProp, int rawValue) {
|
||||
return 100.0 * (rawValue - rawProp.minimum) /
|
||||
(rawProp.maximum - rawProp.minimum);
|
||||
}
|
||||
|
||||
int UsbCameraImpl::PercentageToRaw(const PropertyData& rawProp,
|
||||
int percentValue) {
|
||||
return rawProp.minimum +
|
||||
(rawProp.maximum - rawProp.minimum) * (percentValue / 100.0);
|
||||
}
|
||||
|
||||
void UsbCameraImpl::UpdatePropertyValue(int property, bool setString, int value,
|
||||
llvm::StringRef valueStr) {
|
||||
auto prop = static_cast<PropertyData*>(GetProperty(property));
|
||||
if (!prop) return;
|
||||
|
||||
if (setString)
|
||||
prop->SetValue(valueStr);
|
||||
else
|
||||
prop->SetValue(value);
|
||||
|
||||
// Only notify updates after we've notified created
|
||||
if (m_properties_cached)
|
||||
Notifier::GetInstance().NotifySourceProperty(
|
||||
*this, CS_SOURCE_PROPERTY_VALUE_UPDATED, property, prop->propKind,
|
||||
prop->value, prop->valueStr);
|
||||
}
|
||||
|
||||
#ifdef VIDIOC_QUERY_EXT_CTRL
|
||||
UsbCameraImpl::PropertyData::PropertyData(
|
||||
const struct v4l2_query_ext_ctrl& ctrl)
|
||||
: PropertyBase(llvm::StringRef{}, CS_PROP_NONE, ctrl.minimum, ctrl.maximum,
|
||||
ctrl.step, ctrl.default_value, 0),
|
||||
: PropertyBase(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:
|
||||
@@ -122,10 +180,15 @@ UsbCameraImpl::PropertyData::PropertyData(
|
||||
#endif
|
||||
|
||||
UsbCameraImpl::PropertyData::PropertyData(const struct v4l2_queryctrl& ctrl)
|
||||
: PropertyBase(llvm::StringRef{}, CS_PROP_NONE, ctrl.minimum, ctrl.maximum,
|
||||
ctrl.step, ctrl.default_value, 0),
|
||||
: PropertyBase(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:
|
||||
@@ -647,9 +710,10 @@ void UsbCameraImpl::DeviceConnect() {
|
||||
SDEBUG3("restoring settings");
|
||||
std::unique_lock<std::mutex> lock2(m_mutex);
|
||||
for (std::size_t i = 0; i < m_propertyData.size(); ++i) {
|
||||
const auto& prop = m_propertyData[i];
|
||||
if (!prop || !prop->valueSet) continue;
|
||||
if (!DeviceSetProperty(lock2, static_cast<const PropertyData&>(*prop)))
|
||||
const auto prop =
|
||||
static_cast<const PropertyData*>(m_propertyData[i].get());
|
||||
if (!prop || !prop->valueSet || prop->percentage) continue;
|
||||
if (!DeviceSetProperty(lock2, *prop))
|
||||
SWARNING("failed to set property " << prop->name);
|
||||
}
|
||||
}
|
||||
@@ -820,66 +884,17 @@ void UsbCameraImpl::DeviceProcessCommands() {
|
||||
}
|
||||
} else if (msg->kind == Message::kCmdSetProperty ||
|
||||
msg->kind == Message::kCmdSetPropertyStr) {
|
||||
bool setString = (msg->kind == Message::kCmdSetPropertyStr);
|
||||
int property = msg->data[0];
|
||||
|
||||
// Look up
|
||||
auto prop = static_cast<PropertyData*>(GetProperty(property));
|
||||
if (!prop) {
|
||||
int value = msg->data[1];
|
||||
CS_StatusValue status =
|
||||
DeviceCmdSetProperty(lock, property, setString, value, msg->dataStr);
|
||||
if (status == CS_OK) {
|
||||
msg->kind = Message::kOk;
|
||||
} else {
|
||||
msg->kind = Message::kError;
|
||||
msg->data[0] = CS_INVALID_PROPERTY;
|
||||
goto done;
|
||||
msg->data[0] = status;
|
||||
}
|
||||
|
||||
// Check kind match
|
||||
if ((msg->kind == Message::kCmdSetPropertyStr &&
|
||||
prop->propKind != CS_PROP_STRING) ||
|
||||
(msg->kind == Message::kCmdSetProperty &&
|
||||
(prop->propKind &
|
||||
(CS_PROP_BOOLEAN | CS_PROP_INTEGER | CS_PROP_ENUM)) == 0)) {
|
||||
msg->kind = Message::kError;
|
||||
msg->data[0] = CS_WRONG_PROPERTY_TYPE;
|
||||
goto done;
|
||||
}
|
||||
|
||||
// If we're connected...
|
||||
int fd = m_fd.load();
|
||||
if (fd >= 0) {
|
||||
// Get into local variables before we release the lock
|
||||
int rv;
|
||||
unsigned id = prop->id;
|
||||
int maximum = prop->maximum;
|
||||
int type = prop->type;
|
||||
|
||||
// Set the property value on the device
|
||||
lock.unlock();
|
||||
if (msg->kind == Message::kCmdSetPropertyStr)
|
||||
rv = SetStringCtrlIoctl(fd, id, maximum, msg->dataStr);
|
||||
else
|
||||
rv =
|
||||
SetIntCtrlIoctl(fd, id, type, static_cast<int64_t>(msg->data[1]));
|
||||
lock.lock();
|
||||
|
||||
if (rv < 0) {
|
||||
msg->kind = Message::kError;
|
||||
msg->data[0] = CS_PROPERTY_WRITE_FAILED;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
// Cache the set value. We need to re-get the pointer due to
|
||||
// releasing the lock.
|
||||
prop = static_cast<PropertyData*>(GetProperty(property));
|
||||
if (msg->kind == Message::kCmdSetPropertyStr)
|
||||
prop->valueStr = msg->dataStr;
|
||||
else
|
||||
prop->value = msg->data[1];
|
||||
prop->valueSet = true;
|
||||
// Only notify updates after we've notified created
|
||||
if (m_properties_cached)
|
||||
Notifier::GetInstance().NotifySourceProperty(
|
||||
*this, CS_SOURCE_PROPERTY_VALUE_UPDATED, property, prop->propKind,
|
||||
prop->value, prop->valueStr);
|
||||
msg->kind = Message::kOk;
|
||||
} else if (msg->kind == Message::kNumSinksChanged ||
|
||||
msg->kind == Message::kNumSinksEnabledChanged) {
|
||||
// These are send-only messages, so recycle here. DestroyMessage needs
|
||||
@@ -893,7 +908,6 @@ void UsbCameraImpl::DeviceProcessCommands() {
|
||||
msg->kind = Message::kNone;
|
||||
}
|
||||
|
||||
done:
|
||||
if (msg) m_responses.emplace_back(std::move(msg));
|
||||
}
|
||||
lock.unlock();
|
||||
@@ -912,21 +926,12 @@ void UsbCameraImpl::DeviceSetMode() {
|
||||
: 0;
|
||||
#endif
|
||||
vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
switch (m_mode.pixelFormat) {
|
||||
case VideoMode::kMJPEG:
|
||||
vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
|
||||
break;
|
||||
case VideoMode::kYUYV:
|
||||
vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
|
||||
break;
|
||||
case VideoMode::kRGB565:
|
||||
vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB565;
|
||||
break;
|
||||
default:
|
||||
SWARNING("could not set format " << m_mode.pixelFormat
|
||||
<< ", defaulting to MJPEG");
|
||||
vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
|
||||
break;
|
||||
vfmt.fmt.pix.pixelformat =
|
||||
FromPixelFormat(static_cast<VideoMode::PixelFormat>(m_mode.pixelFormat));
|
||||
if (vfmt.fmt.pix.pixelformat == 0) {
|
||||
SWARNING("could not set format " << m_mode.pixelFormat
|
||||
<< ", defaulting to MJPEG");
|
||||
vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
|
||||
}
|
||||
vfmt.fmt.pix.width = m_mode.width;
|
||||
vfmt.fmt.pix.height = m_mode.height;
|
||||
@@ -977,21 +982,7 @@ void UsbCameraImpl::DeviceCacheMode() {
|
||||
m_mode = VideoMode{VideoMode::kMJPEG, 320, 240, 30};
|
||||
return;
|
||||
}
|
||||
VideoMode::PixelFormat pixelFormat;
|
||||
switch (vfmt.fmt.pix.pixelformat) {
|
||||
case V4L2_PIX_FMT_MJPEG:
|
||||
pixelFormat = VideoMode::kMJPEG;
|
||||
break;
|
||||
case V4L2_PIX_FMT_YUYV:
|
||||
pixelFormat = VideoMode::kYUYV;
|
||||
break;
|
||||
case V4L2_PIX_FMT_RGB565:
|
||||
pixelFormat = VideoMode::kRGB565;
|
||||
break;
|
||||
default:
|
||||
pixelFormat = VideoMode::kUnknown;
|
||||
break;
|
||||
}
|
||||
VideoMode::PixelFormat pixelFormat = ToPixelFormat(vfmt.fmt.pix.pixelformat);
|
||||
int width = vfmt.fmt.pix.width;
|
||||
int height = vfmt.fmt.pix.height;
|
||||
|
||||
@@ -1066,49 +1057,118 @@ void UsbCameraImpl::DeviceCacheMode() {
|
||||
Notifier::GetInstance().NotifySource(*this, CS_SOURCE_VIDEOMODE_CHANGED);
|
||||
}
|
||||
|
||||
void UsbCameraImpl::DeviceCacheProperty(std::unique_ptr<PropertyData> prop) {
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
int& ndx = m_properties[prop->name];
|
||||
if (ndx == 0) {
|
||||
// get the value
|
||||
lock.unlock();
|
||||
if (!DeviceGetProperty(prop.get()))
|
||||
SWARNING("failed to get property " << prop->name);
|
||||
lock.lock();
|
||||
// create a new index
|
||||
ndx = m_propertyData.size() + 1;
|
||||
m_propertyData.emplace_back(std::move(prop));
|
||||
} else {
|
||||
// merge with existing settings
|
||||
auto prop2 = static_cast<PropertyData*>(GetProperty(ndx));
|
||||
prop->valueSet = prop2->valueSet;
|
||||
prop->value = prop2->value;
|
||||
prop->valueStr = std::move(prop2->valueStr);
|
||||
lock.unlock();
|
||||
if (prop->valueSet) {
|
||||
// set the value if it was previously set
|
||||
if (!DeviceSetProperty(lock, *prop))
|
||||
SWARNING("failed to set property " << prop->name);
|
||||
} else {
|
||||
// otherwise get the value
|
||||
if (!DeviceGetProperty(prop.get()))
|
||||
SWARNING("failed to get property " << prop->name);
|
||||
}
|
||||
lock.lock();
|
||||
m_propertyData[ndx - 1] = std::move(prop);
|
||||
}
|
||||
auto eventProp = static_cast<PropertyData*>(GetProperty(ndx));
|
||||
void UsbCameraImpl::NotifyPropertyCreated(int propIndex, PropertyData& prop) {
|
||||
auto& notifier = Notifier::GetInstance();
|
||||
notifier.NotifySourceProperty(*this, CS_SOURCE_PROPERTY_CREATED, ndx,
|
||||
eventProp->propKind, eventProp->value,
|
||||
eventProp->valueStr);
|
||||
notifier.NotifySourceProperty(*this, CS_SOURCE_PROPERTY_CREATED, propIndex,
|
||||
prop.propKind, prop.value, prop.valueStr);
|
||||
// also notify choices updated event for enum types
|
||||
if (eventProp->propKind == CS_PROP_ENUM)
|
||||
if (prop.propKind == CS_PROP_ENUM)
|
||||
notifier.NotifySourceProperty(*this, CS_SOURCE_PROPERTY_CHOICES_UPDATED,
|
||||
ndx, eventProp->propKind, eventProp->value,
|
||||
propIndex, prop.propKind, prop.value,
|
||||
llvm::StringRef{});
|
||||
}
|
||||
|
||||
void UsbCameraImpl::DeviceCacheProperty(std::unique_ptr<PropertyData> 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
|
||||
// to rename this one as well as create/cache the percentage version.
|
||||
//
|
||||
// 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;
|
||||
if (IsPercentageProperty(rawProp->name)) {
|
||||
perProp = llvm::make_unique<PropertyData>(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));
|
||||
|
||||
int* perIndex = perProp ? &m_properties[perProp->name] : nullptr;
|
||||
bool newPer = !perIndex || *perIndex == 0;
|
||||
PropertyData* oldPerProp =
|
||||
newPer ? nullptr : static_cast<PropertyData*>(GetProperty(*perIndex));
|
||||
|
||||
if (oldRawProp && oldRawProp->valueSet) {
|
||||
// Merge existing raw setting and set percentage from it
|
||||
rawProp->SetValue(oldRawProp->value);
|
||||
rawProp->valueStr = std::move(oldRawProp->valueStr);
|
||||
|
||||
if (perProp) {
|
||||
perProp->SetValue(RawToPercentage(*rawProp, rawProp->value));
|
||||
perProp->valueStr = rawProp->valueStr; // copy
|
||||
}
|
||||
} else if (oldPerProp && oldPerProp->valueSet) {
|
||||
// Merge existing percentage setting and set raw from it
|
||||
perProp->SetValue(oldPerProp->value);
|
||||
perProp->valueStr = std::move(oldPerProp->valueStr);
|
||||
|
||||
rawProp->SetValue(PercentageToRaw(*rawProp, perProp->value));
|
||||
rawProp->valueStr = perProp->valueStr; // copy
|
||||
} else {
|
||||
// Read current raw value and set percentage from it
|
||||
lock.unlock();
|
||||
if (!DeviceGetProperty(rawProp.get()))
|
||||
SWARNING("failed to get property " << rawProp->name);
|
||||
lock.lock();
|
||||
|
||||
if (perProp) {
|
||||
perProp->SetValue(RawToPercentage(*rawProp, rawProp->value));
|
||||
perProp->valueStr = rawProp->valueStr; // copy
|
||||
}
|
||||
}
|
||||
|
||||
// Set value on device if user-configured
|
||||
if (rawProp->valueSet) {
|
||||
if (!DeviceSetProperty(lock, *rawProp))
|
||||
SWARNING("failed to set property " << rawProp->name);
|
||||
}
|
||||
|
||||
// Update pointers since we released the lock
|
||||
rawIndex = &m_properties[rawProp->name];
|
||||
perIndex = perProp ? &m_properties[perProp->name] : nullptr;
|
||||
|
||||
// Get pointers before we move the std::unique_ptr values
|
||||
auto rawPropPtr = rawProp.get();
|
||||
auto perPropPtr = perProp.get();
|
||||
|
||||
if (newRaw) {
|
||||
// create a new index
|
||||
*rawIndex = m_propertyData.size() + 1;
|
||||
m_propertyData.emplace_back(std::move(rawProp));
|
||||
} else {
|
||||
// update
|
||||
m_propertyData[*rawIndex - 1] = std::move(rawProp);
|
||||
}
|
||||
|
||||
// Finish setting up percentage property
|
||||
if (perProp) {
|
||||
perProp->propPair = *rawIndex;
|
||||
perProp->defaultValue =
|
||||
RawToPercentage(*rawPropPtr, rawPropPtr->defaultValue);
|
||||
|
||||
if (newPer) {
|
||||
// create a new index
|
||||
*perIndex = m_propertyData.size() + 1;
|
||||
m_propertyData.emplace_back(std::move(perProp));
|
||||
} else if (perIndex) {
|
||||
// update
|
||||
m_propertyData[*perIndex - 1] = std::move(perProp);
|
||||
}
|
||||
|
||||
// Tell raw property where to find percentage property
|
||||
rawPropPtr->propPair = *perIndex;
|
||||
}
|
||||
|
||||
NotifyPropertyCreated(*rawIndex, *rawPropPtr);
|
||||
if (perPropPtr) NotifyPropertyCreated(*perIndex, *perPropPtr);
|
||||
}
|
||||
|
||||
void UsbCameraImpl::DeviceCacheProperties() {
|
||||
int fd = m_fd.load();
|
||||
if (fd < 0) return;
|
||||
@@ -1213,6 +1273,14 @@ bool UsbCameraImpl::DeviceGetProperty(PropertyData* prop) {
|
||||
|
||||
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;
|
||||
@@ -1224,7 +1292,6 @@ bool UsbCameraImpl::DeviceSetProperty(std::unique_lock<std::mutex>& lock,
|
||||
case CS_PROP_ENUM:
|
||||
{
|
||||
int type = prop.type;
|
||||
int value = prop.value;
|
||||
lock.unlock();
|
||||
rv = SetIntCtrlIoctl(fd, id, type, value);
|
||||
lock.lock();
|
||||
@@ -1233,7 +1300,6 @@ bool UsbCameraImpl::DeviceSetProperty(std::unique_lock<std::mutex>& lock,
|
||||
case CS_PROP_STRING:
|
||||
{
|
||||
int maximum = prop.maximum;
|
||||
llvm::SmallString<128> valueStr{prop.valueStr};
|
||||
lock.unlock();
|
||||
rv = SetStringCtrlIoctl(fd, id, maximum, valueStr);
|
||||
lock.lock();
|
||||
@@ -1246,6 +1312,54 @@ bool UsbCameraImpl::DeviceSetProperty(std::unique_lock<std::mutex>& lock,
|
||||
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));
|
||||
if (!prop) return CS_INVALID_PROPERTY;
|
||||
|
||||
// If setting before we get, guess initial type based on set
|
||||
if (prop->propKind == CS_PROP_NONE) {
|
||||
if (setString)
|
||||
prop->propKind = CS_PROP_STRING;
|
||||
else
|
||||
prop->propKind = CS_PROP_INTEGER;
|
||||
}
|
||||
|
||||
// Check kind match
|
||||
if ((setString && prop->propKind != CS_PROP_STRING) ||
|
||||
(!setString &&
|
||||
(prop->propKind & (CS_PROP_BOOLEAN | CS_PROP_INTEGER | CS_PROP_ENUM)) ==
|
||||
0))
|
||||
return CS_WRONG_PROPERTY_TYPE;
|
||||
|
||||
// Handle percentage property
|
||||
int percentageProperty = prop->propPair;
|
||||
int percentageValue = value;
|
||||
if (percentageProperty != 0) {
|
||||
if (prop->percentage) {
|
||||
std::swap(percentageProperty, property);
|
||||
prop = static_cast<PropertyData*>(GetProperty(property));
|
||||
value = PercentageToRaw(*prop, percentageValue);
|
||||
} else {
|
||||
percentageValue = RawToPercentage(*prop, value);
|
||||
}
|
||||
}
|
||||
|
||||
// Actually set the new value on the device (if possible)
|
||||
if (!DeviceSetProperty(lock, *prop, value, valueStr))
|
||||
return CS_PROPERTY_WRITE_FAILED;
|
||||
|
||||
// Cache the set values
|
||||
UpdatePropertyValue(property, setString, value, valueStr);
|
||||
if (percentageProperty != 0)
|
||||
UpdatePropertyValue(percentageProperty, setString, percentageValue,
|
||||
valueStr);
|
||||
|
||||
return CS_OK;
|
||||
}
|
||||
|
||||
std::unique_ptr<UsbCameraImpl::Message> UsbCameraImpl::SendAndWait(
|
||||
std::unique_ptr<Message> msg) const {
|
||||
int fd = m_command_fd.load();
|
||||
@@ -1303,6 +1417,11 @@ void UsbCameraImpl::Send(std::unique_ptr<Message> msg) const {
|
||||
eventfd_write(fd, 1);
|
||||
}
|
||||
|
||||
std::unique_ptr<SourceImpl::PropertyBase> UsbCameraImpl::CreateEmptyProperty(
|
||||
llvm::StringRef name) const {
|
||||
return llvm::make_unique<PropertyData>(name);
|
||||
}
|
||||
|
||||
bool UsbCameraImpl::CacheProperties(CS_Status* status) const {
|
||||
// Wake up camera thread; this will try to reconnect
|
||||
auto msg = CreateMessage(Message::kNone);
|
||||
|
||||
@@ -53,6 +53,22 @@ class UsbCameraImpl : public SourceImpl {
|
||||
class PropertyData : public PropertyBase {
|
||||
public:
|
||||
PropertyData() = default;
|
||||
PropertyData(llvm::StringRef name_) : PropertyBase{name_} {}
|
||||
|
||||
// Normalized property constructor
|
||||
PropertyData(llvm::StringRef name_, int rawIndex_,
|
||||
const PropertyData& rawProp, int defaultValue_, int value_)
|
||||
: PropertyBase(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);
|
||||
@@ -60,8 +76,14 @@ class UsbCameraImpl : public SourceImpl {
|
||||
PropertyData(const struct v4l2_queryctrl& ctrl);
|
||||
#endif
|
||||
|
||||
unsigned id; // implementation-level id
|
||||
int type; // implementation type, not CS_PropertyKind!
|
||||
// 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
|
||||
@@ -89,6 +111,9 @@ class UsbCameraImpl : public SourceImpl {
|
||||
};
|
||||
|
||||
protected:
|
||||
std::unique_ptr<PropertyBase> CreateEmptyProperty(
|
||||
llvm::StringRef name) const override;
|
||||
|
||||
// Cache properties. Immediately successful if properties are already cached.
|
||||
// If they are not, tries to connect to the camera to do so; returns false and
|
||||
// sets status to CS_SOURCE_IS_DISCONNECTED if that too fails.
|
||||
@@ -126,12 +151,27 @@ class UsbCameraImpl : public SourceImpl {
|
||||
void DeviceSetMode();
|
||||
void DeviceSetFPS();
|
||||
void DeviceCacheMode();
|
||||
void DeviceCacheProperty(std::unique_ptr<PropertyData> prop);
|
||||
void DeviceCacheProperty(std::unique_ptr<PropertyData> 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,
|
||||
int property, bool setString, int value,
|
||||
llvm::StringRef valueStr);
|
||||
|
||||
// Property helper functions
|
||||
int RawToPercentage(const PropertyData& rawProp, int rawValue);
|
||||
int PercentageToRaw(const PropertyData& rawProp, int percentValue);
|
||||
void UpdatePropertyValue(int property, bool setString, int value,
|
||||
llvm::StringRef valueStr);
|
||||
void NotifyPropertyCreated(int propIndex, PropertyData& prop);
|
||||
|
||||
//
|
||||
// Variables only used within camera thread
|
||||
|
||||
Reference in New Issue
Block a user