Skip to content

Commit 3be8d5a

Browse files
committed
Add support for 5-nibble pan/tilt position commands (#17)
1 parent 702f80d commit 3be8d5a

File tree

4 files changed

+99
-28
lines changed

4 files changed

+99
-28
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Besides recalling a pre-made preset, this plugin supports a few more control ope
2020

2121
This plugin requires the camera to support Visca over IP via UDP.
2222
It follows the specification as designed by Sony and also supports the PTZOptics variant of Visca.
23-
This plugin is confirmed to work with at Avonic, BZB Gear, Everet, GlowStream, JVC, PTZOptics and Zowietek cameras.
23+
This plugin is confirmed to work with at Avonic, Canon, BZB Gear, Everet, GlowStream, JVC, PTZOptics and Zowietek cameras.
2424
Others may work as well.
2525

2626
Also visit https://obsproject.com/forum/resources/control-visca-over-ip-based-cameras.1173/ for more information.

libvisca.lua

+65-27
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,7 @@ Visca.compatibility = {
273273
-- When the first preset is 1, leave it nil (or set to 0).
274274
-- When the first preset is 0, set the offset to 1.
275275
-- The preset recalled at the camera is 'preset - <preset_nr_offset>'
276+
pantilt_pan_bytes = 4 -- The number of bytes used for the pan argument in absolute pan/tilt commands
276277
}
277278

278279

@@ -431,16 +432,32 @@ function Visca.PayloadReply:get_inquiry_data_for(inquiry_payload)
431432
end
432433
elseif category == Visca.categories.pan_tilter then
433434
if inquiry_command == Visca.inquiry_commands.pantilt_position then
434-
data = {
435-
pan = bit.lshift(bit.band(self.arguments[1] or 0, 0x0F), 12) +
436-
bit.lshift(bit.band(self.arguments[2] or 0, 0x0F), 8) +
437-
bit.lshift(bit.band(self.arguments[3] or 0, 0x0F), 4) +
438-
bit.band(self.arguments[4] or 0, 0x0F),
439-
tilt = bit.lshift(bit.band(self.arguments[5] or 0, 0x0F), 12) +
440-
bit.lshift(bit.band(self.arguments[6] or 0, 0x0F), 8) +
441-
bit.lshift(bit.band(self.arguments[7] or 0, 0x0F), 4) +
442-
bit.band(self.arguments[8] or 0, 0x0F)
443-
}
435+
if self.argument_cnt == 8 then
436+
data = {
437+
pantilt_pan_bytes = 4,
438+
pan = bit.lshift(bit.band(self.arguments[1] or 0, 0x0F), 12) +
439+
bit.lshift(bit.band(self.arguments[2] or 0, 0x0F), 8) +
440+
bit.lshift(bit.band(self.arguments[3] or 0, 0x0F), 4) +
441+
bit.band(self.arguments[4] or 0, 0x0F),
442+
tilt = bit.lshift(bit.band(self.arguments[5] or 0, 0x0F), 12) +
443+
bit.lshift(bit.band(self.arguments[6] or 0, 0x0F), 8) +
444+
bit.lshift(bit.band(self.arguments[7] or 0, 0x0F), 4) +
445+
bit.band(self.arguments[8] or 0, 0x0F)
446+
}
447+
elseif self.argument_cnt == 9 then
448+
data = {
449+
pantilt_pan_bytes = 5,
450+
pan = bit.lshift(bit.band(self.arguments[1] or 0, 0x0F), 16) +
451+
bit.lshift(bit.band(self.arguments[2] or 0, 0x0F), 12) +
452+
bit.lshift(bit.band(self.arguments[3] or 0, 0x0F), 8) +
453+
bit.lshift(bit.band(self.arguments[4] or 0, 0x0F), 4) +
454+
bit.band(self.arguments[5] or 0, 0x0F),
455+
tilt = bit.lshift(bit.band(self.arguments[6] or 0, 0x0F), 12) +
456+
bit.lshift(bit.band(self.arguments[7] or 0, 0x0F), 8) +
457+
bit.lshift(bit.band(self.arguments[8] or 0, 0x0F), 4) +
458+
bit.band(self.arguments[9] or 0, 0x0F)
459+
}
460+
end
444461
end
445462
end
446463

@@ -1347,23 +1364,44 @@ function Visca.Connection:Cam_PanTilt_Absolute(speed, pan, tilt)
13471364

13481365
local msg = Visca.Message.new()
13491366
msg.payload_type = Visca.payload_types.visca_command
1350-
msg.payload = {
1351-
Visca.packet_consts.req_addr_base + bit.band(Visca.default_camera_nr or 1, 0x0F),
1352-
Visca.packet_consts.command,
1353-
Visca.categories.pan_tilter,
1354-
Visca.commands.pantilt_absolute,
1355-
speed, -- Pan speed
1356-
speed, -- Tilt speed
1357-
bit.band(bit.rshift(pan, 12), 0x0F),
1358-
bit.band(bit.rshift(pan, 8), 0x0F),
1359-
bit.band(bit.rshift(pan, 4), 0x0F),
1360-
bit.band(pan, 0x0F),
1361-
bit.band(bit.rshift(tilt, 12), 0x0F),
1362-
bit.band(bit.rshift(tilt, 8), 0x0F),
1363-
bit.band(bit.rshift(tilt, 4), 0x0F),
1364-
bit.band(tilt, 0x0F),
1365-
Visca.packet_consts.terminator
1366-
}
1367+
if not self.compatibility.pantilt_pan_bytes or self.compatibility.pantilt_pan_bytes == 4 then
1368+
msg.payload = {
1369+
Visca.packet_consts.req_addr_base + bit.band(Visca.default_camera_nr or 1, 0x0F),
1370+
Visca.packet_consts.command,
1371+
Visca.categories.pan_tilter,
1372+
Visca.commands.pantilt_absolute,
1373+
speed, -- Pan speed
1374+
speed, -- Tilt speed
1375+
bit.band(bit.rshift(pan, 12), 0x0F),
1376+
bit.band(bit.rshift(pan, 8), 0x0F),
1377+
bit.band(bit.rshift(pan, 4), 0x0F),
1378+
bit.band(pan, 0x0F),
1379+
bit.band(bit.rshift(tilt, 12), 0x0F),
1380+
bit.band(bit.rshift(tilt, 8), 0x0F),
1381+
bit.band(bit.rshift(tilt, 4), 0x0F),
1382+
bit.band(tilt, 0x0F),
1383+
Visca.packet_consts.terminator
1384+
}
1385+
elseif self.compatibility.pantilt_pan_bytes == 5 then
1386+
msg.payload = {
1387+
Visca.packet_consts.req_addr_base + bit.band(Visca.default_camera_nr or 1, 0x0F),
1388+
Visca.packet_consts.command,
1389+
Visca.categories.pan_tilter,
1390+
Visca.commands.pantilt_absolute,
1391+
speed, -- Pan/Tilt speed
1392+
00, -- Fixed
1393+
bit.band(bit.rshift(pan, 16), 0x0F),
1394+
bit.band(bit.rshift(pan, 12), 0x0F),
1395+
bit.band(bit.rshift(pan, 8), 0x0F),
1396+
bit.band(bit.rshift(pan, 4), 0x0F),
1397+
bit.band(pan, 0x0F),
1398+
bit.band(bit.rshift(tilt, 12), 0x0F),
1399+
bit.band(bit.rshift(tilt, 8), 0x0F),
1400+
bit.band(bit.rshift(tilt, 4), 0x0F),
1401+
bit.band(tilt, 0x0F),
1402+
Visca.packet_consts.terminator
1403+
}
1404+
end
13671405

13681406
return self:send(msg)
13691407
end

obs-visca-control.lua

+4
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,10 @@ local function open_visca_connection(camera_id)
501501
table.insert(ptz_vals, "Zoom: n/a (-)")
502502
end
503503

504+
if reply_data.pantilt_pan_bytes then
505+
connection:set_compatibility({pantilt_pan_bytes = reply_data.pantilt_pan_bytes})
506+
end
507+
504508
for scene_name, source_name, source_settings, _ in
505509
get_plugin_settings_from_scene(plugin_scene_type.Preview, camera_id) do
506510
if source_settings then

test/libvisca_test.lua

+29
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,17 @@ function test_pantilt()
221221
lunit.assert_equal(23, t_size)
222222
lunit.assert_equal(Visca.limits.PAN_MIN_SPEED, string.byte(t_data, 13))
223223
lunit.assert_equal(Visca.limits.PAN_MIN_SPEED, string.byte(t_data, 14))
224+
lunit.assert_equal(0x0F, string.byte(t_data, 15))
225+
lunit.assert_equal(0x0C, string.byte(t_data, 16))
226+
227+
clear_transmission_queue(connection)
228+
connection:set_compatibility({pantilt_pan_bytes = 5})
229+
t_size, t_data = connection:Cam_PanTilt_Absolute(0, 0xABCDE, 0x1234)
230+
lunit.assert_equal(24, t_size)
231+
lunit.assert_equal(Visca.limits.PAN_MIN_SPEED, string.byte(t_data, 13))
232+
lunit.assert_equal(0, string.byte(t_data, 14))
233+
lunit.assert_equal(0x0A, string.byte(t_data, 15))
234+
lunit.assert_equal(0x04, string.byte(t_data, 23))
224235
end
225236

226237
function test_zoom()
@@ -335,6 +346,24 @@ function test_reply_parsing_inquiry_brightnes()
335346
lunit.assert_equal(0x69, msg_inq_brightness_data.brightness)
336347
end
337348

349+
function test_reply_parsing_inquiry_pt_position()
350+
local msg_inq_pt4_position = Visca.Message.new():from_data("\x90\x50\x0A\x0B\x0C\x0D\x01\x02\x03\x04\xFF"):dump("msg_inq_pt4_position")
351+
lunit.assert_not_nil(msg_inq_pt4_position.message.reply)
352+
local msg_inq_pt4_position_data = msg_inq_pt4_position.message.reply:get_inquiry_data_for({0,0,Visca.categories.pan_tilter,Visca.inquiry_commands.pantilt_position})
353+
lunit.assert_not_nil(msg_inq_pt4_position_data)
354+
lunit.assert_equal(4, msg_inq_pt4_position_data.pantilt_pan_bytes)
355+
lunit.assert_equal(0xABCD, msg_inq_pt4_position_data.pan)
356+
lunit.assert_equal(0x1234, msg_inq_pt4_position_data.tilt)
357+
358+
local msg_inq_pt5_position = Visca.Message.new():from_data("\x90\x50\x01\x02\x03\x04\x05\x0A\x0B\x0C\x0D\xFF"):dump("msg_inq_pt5_position")
359+
lunit.assert_not_nil(msg_inq_pt5_position.message.reply)
360+
local msg_inq_pt5_position_data = msg_inq_pt5_position.message.reply:get_inquiry_data_for({0,0,Visca.categories.pan_tilter,Visca.inquiry_commands.pantilt_position})
361+
lunit.assert_not_nil(msg_inq_pt5_position_data)
362+
lunit.assert_equal(5, msg_inq_pt5_position_data.pantilt_pan_bytes)
363+
lunit.assert_equal(0x12345, msg_inq_pt5_position_data.pan)
364+
lunit.assert_equal(0xABCD, msg_inq_pt5_position_data.tilt)
365+
end
366+
338367
function test_inquiry()
339368
lunit.assert_true(connection:set_mode(Visca.modes.generic))
340369

0 commit comments

Comments
 (0)