Skip to content

Commit

Permalink
Merge pull request #18 from vwout/17-add-support-for-5-nibble-pantilt…
Browse files Browse the repository at this point in the history
…-position-commands

Add support for 5 nibble pantilt position commands
  • Loading branch information
vwout authored Jul 15, 2024
2 parents 702f80d + 5809311 commit 751be1e
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 41 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Besides recalling a pre-made preset, this plugin supports a few more control ope

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

Also visit https://obsproject.com/forum/resources/control-visca-over-ip-based-cameras.1173/ for more information.
Expand Down
147 changes: 107 additions & 40 deletions libvisca.lua
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ Visca.compatibility = {
-- When the first preset is 1, leave it nil (or set to 0).
-- When the first preset is 0, set the offset to 1.
-- The preset recalled at the camera is 'preset - <preset_nr_offset>'
pantilt_pan_bytes = 4 -- The number of bytes used for the pan argument in absolute pan/tilt commands
}


Expand Down Expand Up @@ -394,6 +395,7 @@ end
function Visca.PayloadReply:get_inquiry_data_for(inquiry_payload)
local _,_,category,inquiry_command = unpack(inquiry_payload)
local data = {}
local unsupported_nr_of_arguments = false

if category == Visca.categories.interface then
if inquiry_command == Visca.inquiry_commands.software_version then
Expand All @@ -409,38 +411,82 @@ function Visca.PayloadReply:get_inquiry_data_for(inquiry_payload)
rom_version = bit.lshift(self.arguments[5] or 0, 8) + (self.arguments[6] or 0),
max_nr_sockets = bit.band(self.arguments[7] or 0, 0x0F),
}
else
unsupported_nr_of_arguments = true
end
end
elseif category == Visca.categories.camera then
if inquiry_command == Visca.inquiry_commands.color_gain then
data = {
color_level = bit.band(self.arguments[4] or 0, 0x0F)
}
if self.argument_cnt == 4 then
data = {
color_level = bit.band(self.arguments[4] or 0, 0x0F)
}
else
unsupported_nr_of_arguments = true
end
elseif inquiry_command == Visca.inquiry_commands.brightness_position then
data = {
brightness = bit.lshift(bit.band(self.arguments[3] or 0, 0x0F), 4) +
bit.band(self.arguments[4] or 0, 0x0F),
}
if self.argument_cnt == 4 then
data = {
brightness = bit.lshift(bit.band(self.arguments[3] or 0, 0x0F), 4) +
bit.band(self.arguments[4] or 0, 0x0F),
}
else
unsupported_nr_of_arguments = true
end
elseif inquiry_command == Visca.inquiry_commands.zoom_position then
data = {
zoom = bit.lshift(bit.band(self.arguments[1] or 0, 0x0F), 12) +
bit.lshift(bit.band(self.arguments[2] or 0, 0x0F), 8) +
bit.lshift(bit.band(self.arguments[3] or 0, 0x0F), 4) +
bit.band(self.arguments[4] or 0, 0x0F),
}
if self.argument_cnt == 4 then
data = {
zoom = bit.lshift(bit.band(self.arguments[1] or 0, 0x0F), 12) +
bit.lshift(bit.band(self.arguments[2] or 0, 0x0F), 8) +
bit.lshift(bit.band(self.arguments[3] or 0, 0x0F), 4) +
bit.band(self.arguments[4] or 0, 0x0F),
}
else
unsupported_nr_of_arguments = true
end
end
elseif category == Visca.categories.pan_tilter then
if inquiry_command == Visca.inquiry_commands.pantilt_position then
data = {
pan = bit.lshift(bit.band(self.arguments[1] or 0, 0x0F), 12) +
bit.lshift(bit.band(self.arguments[2] or 0, 0x0F), 8) +
bit.lshift(bit.band(self.arguments[3] or 0, 0x0F), 4) +
bit.band(self.arguments[4] or 0, 0x0F),
tilt = bit.lshift(bit.band(self.arguments[5] or 0, 0x0F), 12) +
bit.lshift(bit.band(self.arguments[6] or 0, 0x0F), 8) +
bit.lshift(bit.band(self.arguments[7] or 0, 0x0F), 4) +
bit.band(self.arguments[8] or 0, 0x0F)
}
if self.argument_cnt == 8 then
data = {
pantilt_pan_bytes = 4,
pan = bit.lshift(bit.band(self.arguments[1] or 0, 0x0F), 12) +
bit.lshift(bit.band(self.arguments[2] or 0, 0x0F), 8) +
bit.lshift(bit.band(self.arguments[3] or 0, 0x0F), 4) +
bit.band(self.arguments[4] or 0, 0x0F),
tilt = bit.lshift(bit.band(self.arguments[5] or 0, 0x0F), 12) +
bit.lshift(bit.band(self.arguments[6] or 0, 0x0F), 8) +
bit.lshift(bit.band(self.arguments[7] or 0, 0x0F), 4) +
bit.band(self.arguments[8] or 0, 0x0F)
}
elseif self.argument_cnt == 9 then
data = {
pantilt_pan_bytes = 5,
pan = bit.lshift(bit.band(self.arguments[1] or 0, 0x0F), 16) +
bit.lshift(bit.band(self.arguments[2] or 0, 0x0F), 12) +
bit.lshift(bit.band(self.arguments[3] or 0, 0x0F), 8) +
bit.lshift(bit.band(self.arguments[4] or 0, 0x0F), 4) +
bit.band(self.arguments[5] or 0, 0x0F),
tilt = bit.lshift(bit.band(self.arguments[6] or 0, 0x0F), 12) +
bit.lshift(bit.band(self.arguments[7] or 0, 0x0F), 8) +
bit.lshift(bit.band(self.arguments[8] or 0, 0x0F), 4) +
bit.band(self.arguments[9] or 0, 0x0F)
}
else
unsupported_nr_of_arguments = true
end
end
end

if next(data) == nil then
if Visca.debug then
if unsupported_nr_of_arguments then
print(string.format("Unsupported number of arguments received for inquiry %d (%d)",
inquiry_command, self.argument_cnt))
else
print(string.format("Unsupported inquiry type received (%d) for command category %d",
inquiry_command, category))
end
end
end

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

local msg = Visca.Message.new()
msg.payload_type = Visca.payload_types.visca_command
msg.payload = {
Visca.packet_consts.req_addr_base + bit.band(Visca.default_camera_nr or 1, 0x0F),
Visca.packet_consts.command,
Visca.categories.pan_tilter,
Visca.commands.pantilt_absolute,
speed, -- Pan speed
speed, -- Tilt speed
bit.band(bit.rshift(pan, 12), 0x0F),
bit.band(bit.rshift(pan, 8), 0x0F),
bit.band(bit.rshift(pan, 4), 0x0F),
bit.band(pan, 0x0F),
bit.band(bit.rshift(tilt, 12), 0x0F),
bit.band(bit.rshift(tilt, 8), 0x0F),
bit.band(bit.rshift(tilt, 4), 0x0F),
bit.band(tilt, 0x0F),
Visca.packet_consts.terminator
}
if not self.compatibility.pantilt_pan_bytes or self.compatibility.pantilt_pan_bytes == 4 then
msg.payload = {
Visca.packet_consts.req_addr_base + bit.band(Visca.default_camera_nr or 1, 0x0F),
Visca.packet_consts.command,
Visca.categories.pan_tilter,
Visca.commands.pantilt_absolute,
speed, -- Pan speed
speed, -- Tilt speed
bit.band(bit.rshift(pan, 12), 0x0F),
bit.band(bit.rshift(pan, 8), 0x0F),
bit.band(bit.rshift(pan, 4), 0x0F),
bit.band(pan, 0x0F),
bit.band(bit.rshift(tilt, 12), 0x0F),
bit.band(bit.rshift(tilt, 8), 0x0F),
bit.band(bit.rshift(tilt, 4), 0x0F),
bit.band(tilt, 0x0F),
Visca.packet_consts.terminator
}
elseif self.compatibility.pantilt_pan_bytes == 5 then
msg.payload = {
Visca.packet_consts.req_addr_base + bit.band(Visca.default_camera_nr or 1, 0x0F),
Visca.packet_consts.command,
Visca.categories.pan_tilter,
Visca.commands.pantilt_absolute,
speed, -- Pan/Tilt speed
00, -- Fixed
bit.band(bit.rshift(pan, 16), 0x0F),
bit.band(bit.rshift(pan, 12), 0x0F),
bit.band(bit.rshift(pan, 8), 0x0F),
bit.band(bit.rshift(pan, 4), 0x0F),
bit.band(pan, 0x0F),
bit.band(bit.rshift(tilt, 12), 0x0F),
bit.band(bit.rshift(tilt, 8), 0x0F),
bit.band(bit.rshift(tilt, 4), 0x0F),
bit.band(tilt, 0x0F),
Visca.packet_consts.terminator
}
end

return self:send(msg)
end
Expand Down
4 changes: 4 additions & 0 deletions obs-visca-control.lua
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,10 @@ local function open_visca_connection(camera_id)
table.insert(ptz_vals, "Zoom: n/a (-)")
end

if reply_data.pantilt_pan_bytes then
connection:set_compatibility({pantilt_pan_bytes = reply_data.pantilt_pan_bytes})
end

for scene_name, source_name, source_settings, _ in
get_plugin_settings_from_scene(plugin_scene_type.Preview, camera_id) do
if source_settings then
Expand Down
29 changes: 29 additions & 0 deletions test/libvisca_test.lua
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,17 @@ function test_pantilt()
lunit.assert_equal(23, t_size)
lunit.assert_equal(Visca.limits.PAN_MIN_SPEED, string.byte(t_data, 13))
lunit.assert_equal(Visca.limits.PAN_MIN_SPEED, string.byte(t_data, 14))
lunit.assert_equal(0x0F, string.byte(t_data, 15))
lunit.assert_equal(0x0C, string.byte(t_data, 16))

clear_transmission_queue(connection)
connection:set_compatibility({pantilt_pan_bytes = 5})
t_size, t_data = connection:Cam_PanTilt_Absolute(0, 0xABCDE, 0x1234)
lunit.assert_equal(24, t_size)
lunit.assert_equal(Visca.limits.PAN_MIN_SPEED, string.byte(t_data, 13))
lunit.assert_equal(0, string.byte(t_data, 14))
lunit.assert_equal(0x0A, string.byte(t_data, 15))
lunit.assert_equal(0x04, string.byte(t_data, 23))
end

function test_zoom()
Expand Down Expand Up @@ -335,6 +346,24 @@ function test_reply_parsing_inquiry_brightnes()
lunit.assert_equal(0x69, msg_inq_brightness_data.brightness)
end

function test_reply_parsing_inquiry_pt_position()
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")
lunit.assert_not_nil(msg_inq_pt4_position.message.reply)
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})
lunit.assert_not_nil(msg_inq_pt4_position_data)
lunit.assert_equal(4, msg_inq_pt4_position_data.pantilt_pan_bytes)
lunit.assert_equal(0xABCD, msg_inq_pt4_position_data.pan)
lunit.assert_equal(0x1234, msg_inq_pt4_position_data.tilt)

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")
lunit.assert_not_nil(msg_inq_pt5_position.message.reply)
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})
lunit.assert_not_nil(msg_inq_pt5_position_data)
lunit.assert_equal(5, msg_inq_pt5_position_data.pantilt_pan_bytes)
lunit.assert_equal(0x12345, msg_inq_pt5_position_data.pan)
lunit.assert_equal(0xABCD, msg_inq_pt5_position_data.tilt)
end

function test_inquiry()
lunit.assert_true(connection:set_mode(Visca.modes.generic))

Expand Down

0 comments on commit 751be1e

Please sign in to comment.