Skip to content

Commit 751be1e

Browse files
authored
Merge pull request #18 from vwout/17-add-support-for-5-nibble-pantilt-position-commands
Add support for 5 nibble pantilt position commands
2 parents 702f80d + 5809311 commit 751be1e

File tree

4 files changed

+141
-41
lines changed

4 files changed

+141
-41
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

+107-40
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

@@ -394,6 +395,7 @@ end
394395
function Visca.PayloadReply:get_inquiry_data_for(inquiry_payload)
395396
local _,_,category,inquiry_command = unpack(inquiry_payload)
396397
local data = {}
398+
local unsupported_nr_of_arguments = false
397399

398400
if category == Visca.categories.interface then
399401
if inquiry_command == Visca.inquiry_commands.software_version then
@@ -409,38 +411,82 @@ function Visca.PayloadReply:get_inquiry_data_for(inquiry_payload)
409411
rom_version = bit.lshift(self.arguments[5] or 0, 8) + (self.arguments[6] or 0),
410412
max_nr_sockets = bit.band(self.arguments[7] or 0, 0x0F),
411413
}
414+
else
415+
unsupported_nr_of_arguments = true
412416
end
413417
end
414418
elseif category == Visca.categories.camera then
415419
if inquiry_command == Visca.inquiry_commands.color_gain then
416-
data = {
417-
color_level = bit.band(self.arguments[4] or 0, 0x0F)
418-
}
420+
if self.argument_cnt == 4 then
421+
data = {
422+
color_level = bit.band(self.arguments[4] or 0, 0x0F)
423+
}
424+
else
425+
unsupported_nr_of_arguments = true
426+
end
419427
elseif inquiry_command == Visca.inquiry_commands.brightness_position then
420-
data = {
421-
brightness = bit.lshift(bit.band(self.arguments[3] or 0, 0x0F), 4) +
422-
bit.band(self.arguments[4] or 0, 0x0F),
423-
}
428+
if self.argument_cnt == 4 then
429+
data = {
430+
brightness = bit.lshift(bit.band(self.arguments[3] or 0, 0x0F), 4) +
431+
bit.band(self.arguments[4] or 0, 0x0F),
432+
}
433+
else
434+
unsupported_nr_of_arguments = true
435+
end
424436
elseif inquiry_command == Visca.inquiry_commands.zoom_position then
425-
data = {
426-
zoom = bit.lshift(bit.band(self.arguments[1] or 0, 0x0F), 12) +
427-
bit.lshift(bit.band(self.arguments[2] or 0, 0x0F), 8) +
428-
bit.lshift(bit.band(self.arguments[3] or 0, 0x0F), 4) +
429-
bit.band(self.arguments[4] or 0, 0x0F),
430-
}
437+
if self.argument_cnt == 4 then
438+
data = {
439+
zoom = bit.lshift(bit.band(self.arguments[1] or 0, 0x0F), 12) +
440+
bit.lshift(bit.band(self.arguments[2] or 0, 0x0F), 8) +
441+
bit.lshift(bit.band(self.arguments[3] or 0, 0x0F), 4) +
442+
bit.band(self.arguments[4] or 0, 0x0F),
443+
}
444+
else
445+
unsupported_nr_of_arguments = true
446+
end
431447
end
432448
elseif category == Visca.categories.pan_tilter then
433449
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-
}
450+
if self.argument_cnt == 8 then
451+
data = {
452+
pantilt_pan_bytes = 4,
453+
pan = bit.lshift(bit.band(self.arguments[1] or 0, 0x0F), 12) +
454+
bit.lshift(bit.band(self.arguments[2] or 0, 0x0F), 8) +
455+
bit.lshift(bit.band(self.arguments[3] or 0, 0x0F), 4) +
456+
bit.band(self.arguments[4] or 0, 0x0F),
457+
tilt = bit.lshift(bit.band(self.arguments[5] or 0, 0x0F), 12) +
458+
bit.lshift(bit.band(self.arguments[6] or 0, 0x0F), 8) +
459+
bit.lshift(bit.band(self.arguments[7] or 0, 0x0F), 4) +
460+
bit.band(self.arguments[8] or 0, 0x0F)
461+
}
462+
elseif self.argument_cnt == 9 then
463+
data = {
464+
pantilt_pan_bytes = 5,
465+
pan = bit.lshift(bit.band(self.arguments[1] or 0, 0x0F), 16) +
466+
bit.lshift(bit.band(self.arguments[2] or 0, 0x0F), 12) +
467+
bit.lshift(bit.band(self.arguments[3] or 0, 0x0F), 8) +
468+
bit.lshift(bit.band(self.arguments[4] or 0, 0x0F), 4) +
469+
bit.band(self.arguments[5] or 0, 0x0F),
470+
tilt = bit.lshift(bit.band(self.arguments[6] or 0, 0x0F), 12) +
471+
bit.lshift(bit.band(self.arguments[7] or 0, 0x0F), 8) +
472+
bit.lshift(bit.band(self.arguments[8] or 0, 0x0F), 4) +
473+
bit.band(self.arguments[9] or 0, 0x0F)
474+
}
475+
else
476+
unsupported_nr_of_arguments = true
477+
end
478+
end
479+
end
480+
481+
if next(data) == nil then
482+
if Visca.debug then
483+
if unsupported_nr_of_arguments then
484+
print(string.format("Unsupported number of arguments received for inquiry %d (%d)",
485+
inquiry_command, self.argument_cnt))
486+
else
487+
print(string.format("Unsupported inquiry type received (%d) for command category %d",
488+
inquiry_command, category))
489+
end
444490
end
445491
end
446492

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

13481394
local msg = Visca.Message.new()
13491395
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-
}
1396+
if not self.compatibility.pantilt_pan_bytes or self.compatibility.pantilt_pan_bytes == 4 then
1397+
msg.payload = {
1398+
Visca.packet_consts.req_addr_base + bit.band(Visca.default_camera_nr or 1, 0x0F),
1399+
Visca.packet_consts.command,
1400+
Visca.categories.pan_tilter,
1401+
Visca.commands.pantilt_absolute,
1402+
speed, -- Pan speed
1403+
speed, -- Tilt speed
1404+
bit.band(bit.rshift(pan, 12), 0x0F),
1405+
bit.band(bit.rshift(pan, 8), 0x0F),
1406+
bit.band(bit.rshift(pan, 4), 0x0F),
1407+
bit.band(pan, 0x0F),
1408+
bit.band(bit.rshift(tilt, 12), 0x0F),
1409+
bit.band(bit.rshift(tilt, 8), 0x0F),
1410+
bit.band(bit.rshift(tilt, 4), 0x0F),
1411+
bit.band(tilt, 0x0F),
1412+
Visca.packet_consts.terminator
1413+
}
1414+
elseif self.compatibility.pantilt_pan_bytes == 5 then
1415+
msg.payload = {
1416+
Visca.packet_consts.req_addr_base + bit.band(Visca.default_camera_nr or 1, 0x0F),
1417+
Visca.packet_consts.command,
1418+
Visca.categories.pan_tilter,
1419+
Visca.commands.pantilt_absolute,
1420+
speed, -- Pan/Tilt speed
1421+
00, -- Fixed
1422+
bit.band(bit.rshift(pan, 16), 0x0F),
1423+
bit.band(bit.rshift(pan, 12), 0x0F),
1424+
bit.band(bit.rshift(pan, 8), 0x0F),
1425+
bit.band(bit.rshift(pan, 4), 0x0F),
1426+
bit.band(pan, 0x0F),
1427+
bit.band(bit.rshift(tilt, 12), 0x0F),
1428+
bit.band(bit.rshift(tilt, 8), 0x0F),
1429+
bit.band(bit.rshift(tilt, 4), 0x0F),
1430+
bit.band(tilt, 0x0F),
1431+
Visca.packet_consts.terminator
1432+
}
1433+
end
13671434

13681435
return self:send(msg)
13691436
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)