Skip to content

Commit 95b874d

Browse files
committed
Refactor PTZControl to track initial and current positions
This commit refactors the PTZControl class hierarchy to initialize and maintain the current PTZ position (pan, tilt, zoom, focus, and autofocus) at startup and update it with every control action. Previously, the PTZUSBCam class managed position state, leading to inconsistencies if the device state changed externally. Now, V4L2Control and DirectShowControl query and store the initial device position during construction and update it internally. Key changes include: - Removing the axis_ids array and PTZAxis enum, replacing them with direct V4L2 control IDs and a PtzUsbCamLimits struct for clarity. - Adding get_ctrl() to V4L2Control and Get() calls in DirectShowControl to fetch initial positions. - Modifying PTZControl methods to update now_pos internally, with getters (e.g., getPan()) for PTZUSBCam to query state. - Adjusting PTZUSBCam to delegate position tracking to PTZControl, removing redundant now_pos and initialization logic. This ensures the plugin reflects the device's actual state at startup and maintains synchronization thereafter. Signed-off-by: Fabio Ferrari <fabioferrari@gmail.com>
1 parent ecd1b90 commit 95b874d

File tree

2 files changed

+177
-152
lines changed

2 files changed

+177
-152
lines changed

src/ptz-usb-cam.cpp

Lines changed: 126 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,15 @@
2121
#include <fcntl.h>
2222
#include <unistd.h>
2323

24-
const std::array<unsigned int, 5> axis_ids = {
25-
V4L2_CID_PAN_ABSOLUTE, V4L2_CID_TILT_ABSOLUTE, V4L2_CID_ZOOM_ABSOLUTE,
26-
V4L2_CID_FOCUS_ABSOLUTE, V4L2_CID_FOCUS_AUTO};
27-
2824
class V4L2Control : public PTZControl {
2925
private:
3026
int fd;
3127
int query_ctrl(unsigned int i, long *pmin, long *pmax)
3228
{
3329
if (fd == -1)
3430
return false;
35-
struct v4l2_queryctrl queryctrl;
36-
memset(&queryctrl, 0, sizeof(queryctrl));
37-
queryctrl.id = axis_ids[i];
31+
struct v4l2_queryctrl queryctrl = {};
32+
queryctrl.id = i;
3833
if (ioctl(fd, VIDIOC_QUERYCTRL, &queryctrl) == -1) {
3934
blog(LOG_ERROR, "VIDIOC_QUERYCTRL failed for axis %d",
4035
i);
@@ -48,16 +43,27 @@ class V4L2Control : public PTZControl {
4843
{
4944
if (fd == -1)
5045
return false;
51-
struct v4l2_control control;
52-
memset(&control, 0, sizeof(control));
53-
control.id = axis_ids[i];
46+
struct v4l2_control control = {};
47+
control.id = i;
5448
control.value = value;
5549
if (ioctl(fd, VIDIOC_S_CTRL, &control) == -1) {
5650
blog(LOG_ERROR, "Failed to set PTZ %d value", i);
5751
return false;
5852
}
5953
return true;
6054
}
55+
int get_ctrl(unsigned int i)
56+
{
57+
if (fd == -1)
58+
return 0;
59+
struct v4l2_control control = {};
60+
control.id = i;
61+
if (ioctl(fd, VIDIOC_G_CTRL, &control) == -1) {
62+
blog(LOG_ERROR, "VIDIOC_G_CTRL failed for axis %d", i);
63+
return 0;
64+
}
65+
return control.value;
66+
}
6167

6268
public:
6369
V4L2Control(const std::string &device)
@@ -69,32 +75,46 @@ class V4L2Control : public PTZControl {
6975
device_path.c_str());
7076
return;
7177
}
72-
query_ctrl(PTZ_PAN, &min[PTZ_PAN], &max[PTZ_PAN]);
73-
query_ctrl(PTZ_TILT, &min[PTZ_TILT], &max[PTZ_TILT]);
74-
query_ctrl(PTZ_ZOOM, &min[PTZ_ZOOM], &max[PTZ_ZOOM]);
75-
query_ctrl(PTZ_FOCUS, &min[PTZ_FOCUS], &max[PTZ_FOCUS]);
78+
query_ctrl(V4L2_CID_PAN_ABSOLUTE, &min.pan, &max.pan);
79+
query_ctrl(V4L2_CID_TILT_ABSOLUTE, &min.tilt, &max.tilt);
80+
query_ctrl(V4L2_CID_ZOOM_ABSOLUTE, &min.zoom, &max.zoom);
81+
query_ctrl(V4L2_CID_FOCUS_ABSOLUTE, &min.focus, &max.focus);
82+
now_pos.pan =
83+
static_cast<double>(get_ctrl(V4L2_CID_PAN_ABSOLUTE)) /
84+
max.pan;
85+
now_pos.tilt =
86+
static_cast<double>(get_ctrl(V4L2_CID_TILT_ABSOLUTE)) /
87+
max.tilt;
88+
now_pos.zoom =
89+
static_cast<double>(get_ctrl(V4L2_CID_ZOOM_ABSOLUTE)) /
90+
max.zoom;
91+
now_pos.focus =
92+
static_cast<double>(get_ctrl(V4L2_CID_FOCUS_ABSOLUTE)) /
93+
max.focus;
94+
now_pos.focusAuto = get_ctrl(V4L2_CID_FOCUS_AUTO);
7695
}
7796
bool internal_pan(long value) override
7897
{
79-
return set_ctrl(PTZ_PAN, value);
98+
return set_ctrl(V4L2_CID_PAN_ABSOLUTE, value);
8099
}
81100
bool internal_tilt(long value) override
82101
{
83-
return set_ctrl(PTZ_TILT, value);
102+
return set_ctrl(V4L2_CID_TILT_ABSOLUTE, value);
84103
}
85104
bool internal_zoom(long value) override
86105
{
87-
return set_ctrl(PTZ_ZOOM, value);
106+
return set_ctrl(V4L2_CID_ZOOM_ABSOLUTE, value);
88107
}
89-
bool internal_focus(long value) override
108+
bool internal_focus(bool auto_focus, long value) override
90109
{
91-
return set_ctrl(PTZ_FOCUS, value);
92-
}
93-
bool setAutoFocus(bool enabled) override
94-
{
95-
if (fd == -1)
96-
return false;
97-
return set_ctrl(PTZ_FOCUS_AUTO, enabled ? 1 : 0);
110+
if (auto_focus) {
111+
return set_ctrl(V4L2_CID_FOCUS_AUTO, 1);
112+
} else {
113+
if (get_ctrl(V4L2_CID_FOCUS_AUTO) != 0) {
114+
set_ctrl(V4L2_CID_FOCUS_AUTO, 0);
115+
}
116+
return set_ctrl(V4L2_CID_FOCUS_ABSOLUTE, value);
117+
}
98118
}
99119
~V4L2Control() override
100120
{
@@ -208,31 +228,39 @@ class DirectShowControl : public PTZControl {
208228
}
209229
// blog(LOG_INFO, "Obtained DirectShow filter for device: %s", device_name.c_str());
210230
long step, default_value, flags;
211-
hr = cam_control_->GetRange(CameraControl_Pan, &min[PTZ_PAN],
212-
&max[PTZ_PAN], &step,
213-
&default_value, &flags);
231+
hr = cam_control_->GetRange(CameraControl_Pan, &min.pan,
232+
&max.pan, &step, &default_value,
233+
&flags);
214234
if (!FAILED(hr)) {
215235
hr = cam_control_->GetRange(CameraControl_Tilt,
216-
&min[PTZ_TILT],
217-
&max[PTZ_TILT], &step,
236+
&min.tilt, &max.tilt, &step,
218237
&default_value, &flags);
219238
}
220239
if (!FAILED(hr)) {
221240
hr = cam_control_->GetRange(CameraControl_Zoom,
222-
&min[PTZ_ZOOM],
223-
&max[PTZ_ZOOM], &step,
241+
&min.zoom, &max.zoom, &step,
224242
&default_value, &flags);
225243
}
226244
if (!FAILED(hr)) {
227245
hr = cam_control_->GetRange(CameraControl_Focus,
228-
&min[PTZ_FOCUS],
229-
&max[PTZ_FOCUS], &step,
230-
&default_value, &flags);
246+
&min.focus, &max.focus,
247+
&step, &default_value,
248+
&flags);
231249
}
232250
if (FAILED(hr)) {
233251
blog(LOG_ERROR, "Failed to get ranges: %ld", hr);
234252
return;
235253
}
254+
long pan, tilt, zoom, focus;
255+
cam_control_->Get(CameraControl_Pan, &pan, &flags);
256+
now_pos.pan = static_cast<double>(pan) / max.pan;
257+
cam_control_->Get(CameraControl_Tilt, &tilt, &flags);
258+
now_pos.tilt = static_cast<double>(tilt) / max.tilt;
259+
cam_control_->Get(CameraControl_Zoom, &zoom, &flags);
260+
now_pos.zoom = static_cast<double>(zoom) / max.zoom;
261+
cam_control_->Get(CameraControl_Focus, &focus, &flags);
262+
now_pos.focus = static_cast<double>(focus) / max.focus;
263+
now_pos.focusAuto = (flags & CameraControl_Flags_Auto) != 0;
236264
}
237265
bool internal_pan(long value) override
238266
{
@@ -276,33 +304,14 @@ class DirectShowControl : public PTZControl {
276304
}
277305
return true;
278306
}
279-
bool internal_focus(long value) override
280-
{
281-
if (!cam_control_)
282-
return false;
283-
last_focus = value;
284-
HRESULT hr = cam_control_->Set(CameraControl_Focus, value,
285-
CameraControl_Flags_Manual);
286-
if (FAILED(hr)) {
287-
blog(LOG_ERROR,
288-
"Failed to set Focus: %ld (mapped value: %ld)", hr,
289-
value);
290-
return false;
291-
}
292-
return true;
293-
}
294-
bool setAutoFocus(bool enabled) override
307+
bool internal_focus(bool auto_focus, long focus) override
295308
{
296309
if (!cam_control_)
297310
return false;
311+
auto focus_flag = auto_focus ? CameraControl_Flags_Auto
312+
: CameraControl_Flags_Manual;
298313
HRESULT hr;
299-
if (!enabled) {
300-
hr = cam_control_->Set(CameraControl_Focus, last_focus,
301-
CameraControl_Flags_Manual);
302-
} else {
303-
hr = cam_control_->Set(CameraControl_Focus, 0,
304-
CameraControl_Flags_Auto);
305-
}
314+
hr = cam_control_->Set(CameraControl_Focus, focus, focus_flag);
306315
if (FAILED(hr)) {
307316
blog(LOG_ERROR, "Failed to set AutoFocus: %ld", hr);
308317
return false;
@@ -331,9 +340,6 @@ void PTZUSBCam::ptz_tick_callback(void *param, float seconds)
331340
PTZUSBCam::PTZUSBCam(OBSData config) : PTZDevice(config)
332341
{
333342
set_config(config);
334-
pantilt_abs(0, 0);
335-
zoom_abs(0);
336-
set_autofocus(true);
337343
obs_add_tick_callback(ptz_tick_callback, this);
338344
}
339345

@@ -404,25 +410,7 @@ void PTZUSBCam::do_update()
404410
STATUS_FOCUS_SPEED_CHANGED);
405411
}
406412

407-
void PTZUSBCam::ptz_tick(float seconds)
408-
{
409-
tick_elapsed += seconds;
410-
if (tick_elapsed < 0.05f) // Atualiza a cada 50 ms
411-
return;
412-
if (pan_speed != 0.0 || tilt_speed != 0.0) {
413-
pantilt_rel(pan_speed * tick_elapsed,
414-
tilt_speed * tick_elapsed);
415-
}
416-
if (zoom_speed != 0.0) {
417-
zoom_abs(now_pos.zoom + zoom_speed * tick_elapsed);
418-
}
419-
if (focus_speed != 0.0) {
420-
focus_abs(now_pos.focus + focus_speed * tick_elapsed);
421-
}
422-
tick_elapsed = 0.0f; // Reseta após a atualização
423-
}
424-
425-
void PTZUSBCam::initialize_and_check_ptz_control()
413+
PTZControl *PTZUSBCam::get_ptz_control()
426414
{
427415
std::string video_device_id = "";
428416
OBSSourceAutoRelease src =
@@ -443,7 +431,7 @@ void PTZUSBCam::initialize_and_check_ptz_control()
443431
// already have the device, and it didn't change: nothing to do
444432
if (ptz_control_ != nullptr && ptz_control_->isValid() &&
445433
ptz_control_->getDevicePath() == video_device_id) {
446-
return;
434+
return ptz_control_;
447435
}
448436
blog(LOG_INFO, "Switching PTZ USBUVC device from %s to %s",
449437
ptz_control_ == nullptr ? "null"
@@ -454,31 +442,61 @@ void PTZUSBCam::initialize_and_check_ptz_control()
454442
ptz_control_ = nullptr;
455443
}
456444
if (video_device_id.empty()) {
457-
return;
445+
return nullptr;
458446
}
459447
#ifdef __linux__
460448
ptz_control_ = new V4L2Control(video_device_id);
461449
#endif
462450
#ifdef _WIN32
463451
ptz_control_ = new DirectShowControl(video_device_id);
464452
#endif
453+
if (ptz_control_ == nullptr || !ptz_control_->isValid()) {
454+
return nullptr;
455+
}
456+
return ptz_control_;
465457
}
466458

467-
void PTZUSBCam::pantilt_abs(double pan, double tilt)
459+
void PTZUSBCam::ptz_tick(float seconds)
468460
{
469-
initialize_and_check_ptz_control();
470-
now_pos.pan = std::clamp(pan, -1.0, 1.0);
471-
now_pos.tilt = std::clamp(tilt, -1.0, 1.0);
472-
473-
if (ptz_control_ != nullptr && ptz_control_->isValid()) {
474-
ptz_control_->pan(now_pos.pan);
475-
ptz_control_->tilt(now_pos.tilt);
461+
tick_elapsed += seconds;
462+
if (tick_elapsed < 0.03f)
463+
return;
464+
if (pan_speed != 0.0 || tilt_speed != 0.0) {
465+
pantilt_rel(pan_speed * tick_elapsed,
466+
tilt_speed * tick_elapsed);
467+
}
468+
if (zoom_speed != 0.0) {
469+
auto ptzctrl = get_ptz_control();
470+
if (ptzctrl) {
471+
zoom_abs(ptzctrl->getZoom() +
472+
zoom_speed * tick_elapsed);
473+
}
476474
}
475+
if (focus_speed != 0.0) {
476+
auto ptzctrl = get_ptz_control();
477+
if (ptzctrl) {
478+
focus_abs(ptzctrl->getFocus() +
479+
focus_speed * tick_elapsed);
480+
}
481+
}
482+
tick_elapsed = 0.0f;
483+
}
484+
485+
void PTZUSBCam::pantilt_abs(double pan, double tilt)
486+
{
487+
auto ptzctrl = get_ptz_control();
488+
if (!ptzctrl)
489+
return;
490+
ptzctrl->pan(pan);
491+
ptzctrl->tilt(tilt);
477492
}
478493

479494
void PTZUSBCam::pantilt_rel(double pan, double tilt)
480495
{
481-
pantilt_abs(now_pos.pan + pan, now_pos.tilt + tilt);
496+
auto ptzctrl = get_ptz_control();
497+
if (!ptzctrl)
498+
return;
499+
pantilt_abs(ptzctrl->getPan() + pan, ptzctrl->getTilt() + tilt);
482500
}
483501

484502
void PTZUSBCam::pantilt_home()
@@ -488,30 +506,26 @@ void PTZUSBCam::pantilt_home()
488506

489507
void PTZUSBCam::zoom_abs(double pos)
490508
{
491-
initialize_and_check_ptz_control();
492-
now_pos.zoom = std::clamp(pos, 0.0, 1.0);
493-
if (ptz_control_ != nullptr && ptz_control_->isValid()) {
494-
ptz_control_->zoom(now_pos.zoom);
495-
}
509+
auto ptzctrl = get_ptz_control();
510+
if (!ptzctrl)
511+
return;
512+
ptzctrl->zoom(pos);
496513
}
497514

498515
void PTZUSBCam::focus_abs(double pos)
499516
{
500-
initialize_and_check_ptz_control();
501-
now_pos.focus = std::clamp(pos, 0.0, 1.0);
502-
if (ptz_control_ != nullptr && ptz_control_->isValid()) {
503-
ptz_control_->focus(now_pos.focus);
504-
}
505-
return;
517+
auto ptzctrl = get_ptz_control();
518+
if (!ptzctrl)
519+
return;
520+
ptzctrl->focus(pos);
506521
}
507522

508523
void PTZUSBCam::set_autofocus(bool enabled)
509524
{
510-
initialize_and_check_ptz_control();
511-
now_pos.focusAuto = enabled;
512-
if (ptz_control_ != nullptr && ptz_control_->isValid()) {
513-
ptz_control_->setAutoFocus(enabled);
514-
}
525+
auto ptzctrl = get_ptz_control();
526+
if (!ptzctrl)
527+
return;
528+
ptzctrl->setAutoFocus(enabled);
515529
}
516530

517531
void PTZUSBCam::memory_reset(int i)
@@ -523,15 +537,17 @@ void PTZUSBCam::memory_reset(int i)
523537

524538
void PTZUSBCam::memory_set(int i)
525539
{
526-
presets[i] = now_pos;
540+
auto ptzctrl = get_ptz_control();
541+
if (!ptzctrl)
542+
return;
543+
presets[i] = ptzctrl->getPosition();
527544
}
528545

529546
void PTZUSBCam::memory_recall(int i)
530547
{
531548
if (!presets.contains(i))
532549
return;
533-
534-
now_pos = presets[i];
550+
auto now_pos = presets[i];
535551
pantilt_abs(now_pos.pan, now_pos.tilt);
536552
zoom_abs(now_pos.zoom);
537553
set_autofocus(now_pos.focusAuto);

0 commit comments

Comments
 (0)