From 3be8d5a2c504f45b5c036ee32b2ebf97c41b5655 Mon Sep 17 00:00:00 2001 From: vwout Date: Thu, 11 Jul 2024 22:38:40 +0200 Subject: [PATCH] Add support for 5-nibble pan/tilt position commands (#17) --- README.md | 2 +- libvisca.lua | 92 +++++++++++++++++++++++++++++------------- obs-visca-control.lua | 4 ++ test/libvisca_test.lua | 29 +++++++++++++ 4 files changed, 99 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 9e9d834..3fe764b 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/libvisca.lua b/libvisca.lua index 0cb53ed..cee5330 100644 --- a/libvisca.lua +++ b/libvisca.lua @@ -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 - ' + pantilt_pan_bytes = 4 -- The number of bytes used for the pan argument in absolute pan/tilt commands } @@ -431,16 +432,32 @@ function Visca.PayloadReply:get_inquiry_data_for(inquiry_payload) 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) + } + end end end @@ -1347,23 +1364,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 diff --git a/obs-visca-control.lua b/obs-visca-control.lua index 81e7f38..9885089 100644 --- a/obs-visca-control.lua +++ b/obs-visca-control.lua @@ -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 diff --git a/test/libvisca_test.lua b/test/libvisca_test.lua index 9a6d9b3..02267ef 100644 --- a/test/libvisca_test.lua +++ b/test/libvisca_test.lua @@ -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() @@ -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))