obs = obslua
Visca = require("libvisca")
plugin_info = {
name = "Visca Camera Control",
version = "1.4",
url = "https://github.com/vwout/obs-visca-control",
description = "Camera control via Visca over IP",
author = "vwout"
}
plugin_settings = {}
plugin_def = {}
plugin_def.id = "Visca_Control"
plugin_def.type = obs.OBS_SOURCE_TYPE_INPUT
plugin_def.output_flags = bit.bor(obs.OBS_SOURCE_CUSTOM_DRAW)
plugin_data = {}
plugin_data.debug = false
plugin_data.active_scene = nil
plugin_data.preview_scene = nil
plugin_data.connections = {}
plugin_data.hotkeys = {}
local actions = {
Camera_Off = 0,
Camera_On = 1,
Preset_Recal = 2,
PanTilt = 3,
Zoom = 4
}
local action_active = {
Program = 1,
Preview = 2,
Always = 3,
}
local function log(fmt, ...)
if plugin_data.debug or obs.obs_data_get_bool(plugin_settings, "debug_logging") then
local info = debug.getinfo(2, "nl")
local func = info.name or "?"
local line = info.currentline
args = {}
for i, a in ipairs(arg or { ... }) do
if type(a) == "table" then
local kvs = None
for k, v in pairs(a) do
if kvs then
kvs = string.format("%s, %s=%s", kvs, k, tostring(v))
else
kvs = string.format("%s=%s", k, tostring(v))
end
end
args[i] = kvs or "-"
else
args[i] = a
end
end
print(string.format("%s (%d): %s", func, line, string.format(fmt, unpack(args))))
end
end
local function create_camera_controls(props, camera_id, settings)
local cams = obs.obs_properties_get(props, "cameras")
if cams then
local cam_prop_prefix = string.format("cam_%d_", camera_id)
local cam_name_suffix = string.format(" (cam %d)", camera_id)
local cam_name = obs.obs_data_get_string(settings, cam_prop_prefix .. "name")
if #cam_name == 0 then
cam_name = string.format("Camera %d", camera_id)
end
obs.obs_property_list_add_int(cams, cam_name, camera_id)
local prop_name = obs.obs_properties_get(props, cam_prop_prefix .. "name")
if prop_name == nil then
obs.obs_properties_add_text(props, cam_prop_prefix .. "name", "Name" .. cam_name_suffix, obs.OBS_TEXT_DEFAULT)
obs.obs_data_set_default_string(plugin_settings, cam_prop_prefix .. "name", cam_name)
end
local prop_address = obs.obs_properties_get(props, cam_prop_prefix .. "address")
if prop_address == nil then
obs.obs_properties_add_text(props, cam_prop_prefix .. "address", "IP Address" .. cam_name_suffix, obs.OBS_TEXT_DEFAULT)
end
local prop_port = obs.obs_properties_get(props, cam_prop_prefix .. "port")
if prop_port == nil then
obs.obs_properties_add_int(props, cam_prop_prefix .. "port", "UDP Port" .. cam_name_suffix, 1025, 65535, 1)
obs.obs_data_set_default_int(plugin_settings, cam_prop_prefix .. "port", Visca.default_port)
end
local prop_mode = obs.obs_properties_add_list(props, cam_prop_prefix .. "mode", "Mode" .. cam_name_suffix, obs.OBS_COMBO_TYPE_LIST, obs.OBS_COMBO_FORMAT_INT)
obs.obs_property_list_add_int(prop_mode, "Generic", Visca.modes.generic)
obs.obs_property_list_add_int(prop_mode, "PTZOptics", Visca.modes.ptzoptics)
obs.obs_data_set_default_int(plugin_settings, cam_prop_prefix .. "mode", Visca.modes.generic)
local prop_presets = obs.obs_properties_get(props, cam_prop_prefix .. "presets")
if prop_presets == nil then
prop_presets = obs.obs_properties_add_editable_list(props, cam_prop_prefix .. "presets", "Presets" .. cam_name_suffix, obs.OBS_EDITABLE_LIST_TYPE_STRINGS, "", "")
end
obs.obs_property_set_modified_callback(prop_presets, prop_presets_validate)
end
end
local function close_visca_connection(camera_id)
local connection = plugin_data.connections[camera_id]
if connection ~= nil then
connection.close()
connection = nil
plugin_data.connections[camera_id] = nil
end
end
local function open_visca_connection(camera_id, camera_address, camera_port, camera_mode)
local connection = plugin_data.connections[camera_id]
if connection == nil then
local connection_error = ""
connection, connection_error = Visca.connect(camera_address, camera_port)
if connection then
if camera_mode then
connection.set_mode(camera_mode)
end
plugin_data.connections[camera_id] = connection
else
log(connection_error)
end
end
return connection
end
local function do_cam_action_start(camera_id, camera_action, action_args)
local cam_prop_prefix = string.format("cam_%d_", camera_id)
local camera_address = obs.obs_data_get_string(plugin_settings, cam_prop_prefix .. "address")
local camera_port = obs.obs_data_get_int(plugin_settings, cam_prop_prefix .. "port")
local camera_mode = obs.obs_data_get_int(plugin_settings, cam_prop_prefix .. "mode")
action_args = action_args or {}
log("Start cam %d @%s action %d (args %s)", camera_id, camera_address, camera_action, action_args)
-- Force close connection before sending On-command to prevent usage of a dead connection
if camera_action == actions.Camera_On then
close_visca_connection(camera_id)
end
local connection = open_visca_connection(camera_id, camera_address, camera_port, camera_mode)
if connection then
if camera_action == actions.Camera_Off then
connection.Cam_Power(false)
-- Force close connection after sending Off-command.
connection.close()
plugin_data.connections[camera_id] = nil
elseif camera_action == actions.Camera_On then
connection.Cam_Power(true)
elseif camera_action == actions.Preset_Recal and action_args.preset then
connection.Cam_Preset_Recall(action_args.preset)
elseif camera_action == actions.PanTilt then
connection.Cam_PanTilt(action_args.direction or Visca.PanTilt_directions.stop, action_args.pantilt_speed or 0x03, action_args.speed or 0x03)
elseif camera_action == actions.Zoom then
connection.Cam_Zoom(action_args.zoom_subcommand or Visca.Zoom_subcommand.stop, action_args.zoom_speed)
end
end
end
local function do_cam_action_stop(camera_id, camera_action, action_args)
local cam_prop_prefix = string.format("cam_%d_", camera_id)
local camera_address = obs.obs_data_get_string(plugin_settings, cam_prop_prefix .. "address")
local camera_port = obs.obs_data_get_int(plugin_settings, cam_prop_prefix .. "port")
local camera_mode = obs.obs_data_get_int(plugin_settings, cam_prop_prefix .. "mode")
action_args = action_args or {}
log("Stop cam %d @%s action %d (arg %s)", camera_id, camera_address, camera_action, action_args)
local connection = open_visca_connection(camera_id, camera_address, camera_port, camera_mode)
if connection then
if camera_action == actions.PanTilt then
connection.Cam_PanTilt(Visca.PanTilt_directions.stop)
elseif camera_action == actions.Zoom then
connection.Cam_Zoom(Visca.Zoom_subcommand.stop)
end
end
end
local function cb_camera_hotkey(pressed, hotkey_data)
if pressed then
do_cam_action_start(hotkey_data.camera_id, hotkey_data.action, hotkey_data.action_args)
else
do_cam_action_stop(hotkey_data.camera_id, hotkey_data.action, hotkey_data.action_args)
end
end
function script_description()
return "" .. plugin_info.description .. "
" ..
"Version: " .. plugin_info.version .. "
" ..
"" .. plugin_info.url .. "
" ..
"Usage:
" ..
"To add a preset in the list, use one the following naming conventions:
" ..
"- <name><separator><preset id>, e.g. 'Stage: 6'
" ..
"- <preset id><separator><name>, e.g. '5 = Pastor'
" ..
"
where <separator> is one of ':', '=' or '-'."
end
function script_update(settings)
plugin_settings = settings
end
function script_save(settings)
for _, hotkey in pairs(plugin_data.hotkeys) do
local a = obs.obs_hotkey_save(hotkey.id)
obs.obs_data_set_array(settings, hotkey.name .. "_hotkey", a)
obs.obs_data_array_release(a)
end
end
function script_load(settings)
local hotkey_actions = {
{ name = "pan_left", descr = "Pan Left", action = actions.PanTilt, action_args = { direction = Visca.PanTilt_directions.left } },
{ name = "pan_right", descr = "Pan Right", action = actions.PanTilt, action_args = { direction = Visca.PanTilt_directions.right } },
{ name = "tilt_up", descr = "Tilt Up", action = actions.PanTilt, action_args = { direction = Visca.PanTilt_directions.up } },
{ name = "tilt_down", descr = "Tilt Down", action = actions.PanTilt, action_args = { direction = Visca.PanTilt_directions.down } },
{ name = "zoom_in", descr = "Zoom In", action = actions.Zoom, action_args = { subcommand = Visca.Zoom_subcommand.tele_standard} },
{ name = "zoom_out", descr = "Zoom Out", action = actions.Zoom, action_args = { subcommand = Visca.Zoom_subcommand.wide_standard} },
{ name = "preset_0", descr = "Preset 0", action = actions.Preset_Recal, action_args = { preset = 0 } },
{ name = "preset_1", descr = "Preset 1", action = actions.Preset_Recal, action_args = { preset = 1 } },
{ name = "preset_2", descr = "Preset 2", action = actions.Preset_Recal, action_args = { preset = 2 } },
{ name = "preset_3", descr = "Preset 3", action = actions.Preset_Recal, action_args = { preset = 3 } },
{ name = "preset_4", descr = "Preset 4", action = actions.Preset_Recal, action_args = { preset = 4 } },
{ name = "preset_5", descr = "Preset 5", action = actions.Preset_Recal, action_args = { preset = 5 } },
{ name = "preset_6", descr = "Preset 6", action = actions.Preset_Recal, action_args = { preset = 6 } },
{ name = "preset_7", descr = "Preset 7", action = actions.Preset_Recal, action_args = { preset = 7 } },
{ name = "preset_8", descr = "Preset 8", action = actions.Preset_Recal, action_args = { preset = 8 } },
{ name = "preset_9", descr = "Preset 9", action = actions.Preset_Recal, action_args = { preset = 9 } },
}
local num_cameras = obs.obs_data_get_int(settings, "num_cameras")
for camera_id = 1, num_cameras do
local cam_prop_prefix = string.format("cam_%d_", camera_id)
local cam_name = obs.obs_data_get_string(settings, cam_prop_prefix .. "name")
if #cam_name == 0 then
cam_name = string.format("Camera %d", camera_id)
end
for _, v in pairs(hotkey_actions) do
local hotkey_name = cam_prop_prefix .. v.name
local hotkey_id = obs.obs_hotkey_register_frontend(hotkey_name, v.descr .. " " .. cam_name, function(pressed)
cb_camera_hotkey(pressed, { name = hotkey_name, camera_id = camera_id, action = v.action, action_args = v.action_args })
end)
local a = obs.obs_data_get_array(settings, hotkey_name .. "_hotkey")
obs.obs_hotkey_load(hotkey_id, a)
obs.obs_data_array_release(a)
table.insert(plugin_data.hotkeys, {
name = hotkey_name,
id = hotkey_id,
camera_id = camera_id,
action = v.action
})
end
end
end
function script_properties()
local props = obs.obs_properties_create()
local num_cams = obs.obs_properties_add_int(props, "num_cameras", "Number of cameras", 0, 8, 1)
obs.obs_property_set_modified_callback(num_cams, prop_num_cams)
local num_cameras = obs.obs_data_get_int(plugin_settings, "num_cameras")
log("num_cameras %d", num_cameras)
local cams = obs.obs_properties_add_list(props, "cameras", "Camera", obs.OBS_COMBO_TYPE_LIST, obs.OBS_COMBO_FORMAT_INT)
for camera_id = 1, num_cameras do
create_camera_controls(props, camera_id, plugin_settings)
end
obs.obs_property_set_modified_callback(cams, prop_set_attrs_values)
obs.obs_properties_add_bool(props, "debug_logging", "Enable verbose logging (debug)")
return props
end
function prop_num_cams(props, property, settings)
local cam_added = false
local num_cameras = obs.obs_data_get_int(plugin_settings, "num_cameras")
log("num_cameras %d", num_cameras)
local cams = obs.obs_properties_get(props, "cameras")
if cams then
local camera_count = obs.obs_property_list_item_count(cams)
if num_cameras > camera_count then
for camera_id = camera_count + 1, num_cameras do
create_camera_controls(props, camera_id, settings)
end
cam_added = true
end
end
return cam_added
end
function prop_set_attrs_values(props, property, settings)
local changed = false
local num_cameras = obs.obs_data_get_int(plugin_settings, "num_cameras")
local cam_idx = obs.obs_data_get_int(settings, "cameras")
if cnt == 0 then
cam_idx = 0
end
for camera_id = 1, num_cameras do
local visible = cam_idx == camera_id
log("%d %d %d", camera_id, cam_idx, visible and 1 or 0)
local cam_prop_prefix = string.format("cam_%d_", camera_id)
local cam_props = { "name", "address", "port", "mode", "presets", "preset_info" }
for _, cam_prop_name in pairs(cam_props) do
local cam_prop = obs.obs_properties_get(props, cam_prop_prefix .. cam_prop_name)
if cam_prop then
if obs.obs_property_visible(cam_prop) ~= visible then
obs.obs_property_set_visible(cam_prop, visible)
changed = true
end
end
end
end
return changed
end
local function parse_preset_value(preset_value)
local preset_name = nil
local preset_id = nil
local regex_patterns = {
"^(.+)%s*[:=-]%s*(%d+)$",
"^(%d+)%s*[:=-]%s*(.+)$"
}
for _, pattern in pairs(regex_patterns) do
local v1 = nil
local v2 = nil
v1, v2 = string.match(preset_value, pattern)
log("match '%s', '%s'", tostring(v1), tostring(v2))
if (v1 ~= nil) and (v2 ~= nil) then
if (tonumber(v1) == nil) and (tonumber(v2) ~= nil) then
preset_name = v1
preset_id = tonumber(v2)
break
elseif (tonumber(v2) == nil) and (tonumber(v1) ~= nil) then
preset_name = v2
preset_id = tonumber(v1)
break
end
end
end
return preset_name, preset_id
end
function prop_presets_validate(props, property, settings)
local presets = obs.obs_data_get_array(settings, obs.obs_property_name(property))
local num_presets = obs.obs_data_array_count(presets)
log("prop_presets_validate %s %d", obs.obs_property_name(property), num_presets)
if num_presets > 0 then
for i = 0, num_presets - 1 do
local preset = obs.obs_data_array_item(presets, i)
--log(obs.obs_data_get_json(preset))
local preset_value = obs.obs_data_get_string(preset, "value")
--log("check %s", preset_value)
local preset_name, preset_id = parse_preset_value(preset_value)
if (preset_name == nil) or (preset_id == nil) then
print("Warning: preset '" .. preset_value .. "' has an unsupported syntax and cannot be used.")
end
end
end
obs.obs_data_array_release(presets)
end
plugin_def.get_name = function()
return plugin_info.name
end
plugin_def.create = function(settings, source)
local data = {}
local source_sh = obs.obs_source_get_signal_handler(source)
obs.signal_handler_connect(source_sh, "hide", function(calldata) source_signal_handler(calldata, { hide = true }) end)
obs.signal_handler_connect(source_sh, "activate", function(calldata) source_signal_handler(calldata, { activate = true }) end)
obs.signal_handler_connect(source_sh, "deactivate", function(calldata) source_signal_handler(calldata, { deactivate = true }) end)
obs.obs_frontend_add_event_callback(fe_callback)
return data
end
plugin_def.destroy = function(source)
for camera_id, connection in pairs(plugin_data.connections) do
if connection ~= nil then
connection.close()
plugin_data.connections[camera_id] = nil
end
end
plugin_data.connections = {}
end
plugin_def.get_properties = function(data)
local props = obs.obs_properties_create()
local num_cameras = obs.obs_data_get_int(plugin_settings, "num_cameras")
local prop_camera = obs.obs_properties_add_list(props, "scene_camera", "Camera", obs.OBS_COMBO_TYPE_LIST, obs.OBS_COMBO_FORMAT_INT)
local prop_action = obs.obs_properties_add_list(props, "scene_action", "Action", obs.OBS_COMBO_TYPE_LIST, obs.OBS_COMBO_FORMAT_INT)
obs.obs_property_list_add_int(prop_action, "Camera Off", actions.Camera_Off)
obs.obs_property_list_add_int(prop_action, "Camera On", actions.Camera_On)
obs.obs_property_list_add_int(prop_action, "Preset Recall", actions.Preset_Recal)
obs.obs_property_list_add_int(prop_action, "Pan/Tilt", actions.PanTilt)
obs.obs_property_list_add_int(prop_action, "Zoom", actions.Zoom)
for camera_id = 1, num_cameras do
local cam_prop_prefix = string.format("cam_%d_", camera_id)
local cam_name_suffix = string.format(" (cam %d)", camera_id)
local cam_name = obs.obs_data_get_string(plugin_settings, cam_prop_prefix .. "name")
if #cam_name == 0 then
cam_name = string.format("Camera %d", camera_id)
end
obs.obs_property_list_add_int(prop_camera, cam_name, camera_id)
local prop_presets = obs.obs_properties_add_list(props, "scene_" .. cam_prop_prefix .. "preset", "Presets" .. cam_name_suffix, obs.OBS_COMBO_TYPE_LIST, obs.OBS_COMBO_FORMAT_INT)
local presets = obs.obs_data_get_array(plugin_settings, cam_prop_prefix .. "presets")
local num_presets = obs.obs_data_array_count(presets)
log("get_properties %s %d", cam_prop_prefix .. "preset", num_presets)
if num_presets > 0 then
local first_preset = true
for i = 0, num_presets - 1 do
local preset = obs.obs_data_array_item(presets, i)
--log(obs.obs_data_get_json(preset))
local preset_value = obs.obs_data_get_string(preset, "value")
--log("check %s", preset_value)
local preset_name, preset_id = parse_preset_value(preset_value)
if (preset_name ~= nil) and (preset_id ~= nil) then
obs.obs_property_list_add_int(prop_presets, preset_name, preset_id)
if first_preset then
obs.obs_data_set_default_int(plugin_settings, "scene_" .. cam_prop_prefix .. "preset", preset_id)
first_preset = false
end
end
end
end
obs.obs_data_array_release(presets)
end
local direction_names = {}
for direction_name in pairs(Visca.PanTilt_directions) do
table.insert(direction_names, direction_name)
end
table.sort(direction_names)
local prop_pantilt_direction = obs.obs_properties_add_list(props, "scene_direction", "Animation Direction", obs.OBS_COMBO_TYPE_LIST, obs.OBS_COMBO_FORMAT_INT)
obs.obs_property_list_add_int(prop_pantilt_direction, "None", 0)
for _, direction_name in ipairs(direction_names) do
obs.obs_property_list_add_int(prop_pantilt_direction, direction_name:gsub("^%l", string.upper), Visca.PanTilt_directions[direction_name])
end
local prop_animation_speed = obs.obs_properties_add_int_slider(props, "scene_speed", "Animation Speed", Visca.limits.PAN_MIN_SPEED, Visca.limits.PAN_MAX_SPEED, 1)
local zoom_subcommand_names = {}
for zoom_subcommand_name in pairs(Visca.Zoom_subcommand) do
table.insert(zoom_subcommand_names, zoom_subcommand_name)
end
table.sort(zoom_subcommand_names)
local prop_zoom_subcommand = obs.obs_properties_add_list(props, "scene_zoom_subcommand", "Zoom Direction", obs.OBS_COMBO_TYPE_LIST, obs.OBS_COMBO_FORMAT_INT)
for _, zoom_subcommand_name in ipairs(zoom_subcommand_names) do
obs.obs_property_list_add_int(prop_zoom_subcommand, zoom_subcommand_name:gsub("^%l", string.upper), Visca.Zoom_subcommand[zoom_subcommand_name])
end
local prop_zoom_speed = obs.obs_properties_add_int_slider(props, "scene_zoom_speed", "Zoom Speed", Visca.limits.ZOOM_MIN_SPEED, Visca.limits.ZOOM_MAX_SPEED, 1)
local prop_active = obs.obs_properties_add_list(props, "scene_active", "Action Active", obs.OBS_COMBO_TYPE_LIST, obs.OBS_COMBO_FORMAT_INT)
obs.obs_property_list_add_int(prop_active, "On Program", action_active.Program)
obs.obs_property_list_add_int(prop_active, "On Preview", action_active.Preview)
obs.obs_property_list_add_int(prop_active, "Always", action_active.Always)
obs.obs_properties_add_bool(props, "preview_exclusive", "Run action on preview only when the camera is not active on program")
--obs.obs_properties_add_button(props, "run_action", "Perform action now", cb_run_action)
obs.obs_property_set_modified_callback(prop_camera, cb_camera_action_changed)
obs.obs_property_set_modified_callback(prop_action, cb_camera_action_changed)
return props
end
local function set_property_visibility(props, name, visible)
local changed = false
local prop = obs.obs_properties_get(props, name)
if prop then
if obs.obs_property_visible(prop) ~= visible then
obs.obs_property_set_visible(prop, visible)
changed = true
end
end
return changed
end
function cb_camera_action_changed(props, property, data)
local changed = false
local num_cameras = obs.obs_property_list_item_count(property)
local scene_camera = obs.obs_data_get_int(data, "scene_camera")
local scene_action = obs.obs_data_get_int(data, "scene_action")
local scene_zoom_subcommand = obs.obs_data_get_int(data, "scene_zoom_subcommand")
if num_cameras == 0 then
scene_camera = 0
end
for camera_id = 1, num_cameras do
local visible = scene_camera == camera_id
if scene_action ~= actions.Preset_Recal then
visible = false
end
changed = set_property_visibility(props, string.format("scene_cam_%d_preset", camera_id), visible) or changed
end
changed = set_property_visibility(props, "scene_direction", scene_action == actions.PanTilt) or changed
changed = set_property_visibility(props, "scene_speed", scene_action == actions.PanTilt) or changed
changed = set_property_visibility(props, "scene_zoom_subcommand", scene_action == actions.Zoom) or changed
changed = set_property_visibility(props, "scene_zoom_speed", scene_action == actions.Zoom) or changed
return changed
end
local function camera_active_in_scene(program, camera_id)
local active = false
local scene_source = program and obs.obs_frontend_get_current_scene() or obs.obs_frontend_get_current_preview_scene()
if scene_source ~= nil then
local scene = obs.obs_scene_from_source(scene_source)
local scene_name = obs.obs_source_get_name(scene_source)
log("Current %s scene is %s", program and "program" or "preview", scene_name or "?")
local scene_items = obs.obs_scene_enum_items(scene)
if scene_items ~= nil then
for _, scene_item in ipairs(scene_items) do
local scene_item_source = obs.obs_sceneitem_get_source(scene_item)
local scene_item_source_id = obs.obs_source_get_unversioned_id(scene_item_source)
if scene_item_source_id == plugin_def.id then
local visible = obs.obs_source_showing(scene_item_source)
if visible then
local item_source_settings = obs.obs_source_get_settings(scene_item_source)
if item_source_settings ~= nil then
local item_source_camera_id = obs.obs_data_get_int(item_source_settings, "scene_camera")
log("Camera ref: %d active on %s: %d", camera_id, program and "program" or "preview", item_source_camera_id)
if camera_id == item_source_camera_id then
active = true
break
end
obs.obs_data_release(item_source_settings)
end
end
end
end
obs.sceneitem_list_release(scene_items)
end
obs.obs_source_release(scene_source)
end
return active
end
local function do_cam_scene_action(settings, start)
local camera_id = obs.obs_data_get_int(settings, "scene_camera")
local scene_action = obs.obs_data_get_int(settings, "scene_action")
local cam_prop_prefix = string.format("cam_%d_", camera_id)
local action_args = {
preset = obs.obs_data_get_int(settings, "scene_" .. cam_prop_prefix .. "preset"),
direction = obs.obs_data_get_int(settings, "scene_direction"),
pantilt_speed = obs.obs_data_get_double(settings, "scene_speed"),
zoom_subcommand = obs.obs_data_get_int(settings, "scene_zoom_subcommand"),
zoom_speed = obs.obs_data_get_double(settings, "scene_zoom_speed")
}
local active = obs.obs_data_get_int(settings, "scene_active")
if start then
do_cam_action_start(camera_id, scene_action, action_args)
else
if not camera_active_in_scene(true, camera_id) and (active == action_active.Program or not camera_active_in_scene(false, camera_id)) then
do_cam_action_stop(camera_id, scene_action, action_args)
end
end
end
function fe_callback(event, data)
if event == obs.OBS_FRONTEND_EVENT_SCENE_CHANGED then
--local scenesource = obs.obs_frontend_get_current_scene()
--log("fe_callback OBS_FRONTEND_EVENT_SCENE_CHANGED to %s", plugin_data.active_scene or "?")
--obs.obs_source_release(scenesource)
elseif event == obs.OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED then
local scenesource = obs.obs_frontend_get_current_preview_scene()
if scenesource ~= nil then
local scene = obs.obs_scene_from_source(scenesource)
local scene_name = obs.obs_source_get_name(scenesource)
if plugin_data.preview_scene ~= scene_name then
plugin_data.preview_scene = scene_name
log("OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED to %s", scene_name or "?")
local scene_items = obs.obs_scene_enum_items(scene)
if scene_items ~= nil then
for _, scene_item in ipairs(scene_items) do
local source = obs.obs_sceneitem_get_source(scene_item)
local source_id = obs.obs_source_get_unversioned_id(source)
if source_id == plugin_def.id then
local settings = obs.obs_source_get_settings(source)
local source_name = obs.obs_source_get_name(source)
local visible = obs.obs_source_showing(source)
if visible then
local do_action = false
local active = obs.obs_data_get_int(settings, "scene_active")
if (active == action_active.Preview) or (active == action_active.Always) then
do_action = true
local preview_exclusive = obs.obs_data_get_bool(settings, "preview_exclusive")
if preview_exclusive then
local preview_camera_id = obs.obs_data_get_int(settings, "scene_camera")
if camera_active_in_scene(true, preview_camera_id) then
do_action = false
end
end
end
if do_action then
log("Preview source '%s'", source_name or "?")
do_cam_scene_action(settings, true)
end
end
obs.obs_data_release(settings)
end
end
end
obs.sceneitem_list_release(scene_items)
end
obs.obs_source_release(scenesource)
end
end
end
function source_signal_handler(calldata, signal)
local source = obs.calldata_source(calldata, "source")
local settings = obs.obs_source_get_settings(source)
local source_name = obs.obs_source_get_name(source)
log("%s source %s", signal.activate and "Activate" or signal.deactivate and "Deactivate" or signal.hide and "Hide" or "?", source_name)
local do_action = false
local active = obs.obs_data_get_int(settings, "scene_active")
if (active == action_active.Program) or (active == action_active.Always) then
do_action = true
end
if do_action then
if signal.activate then do_cam_scene_action(settings, true) end
if signal.deactivate or signal.hide then do_cam_scene_action(settings, false) end
end
obs.obs_data_release(settings)
end
obs.obs_register_source(plugin_def)